Skip to content

plugin handlers traceback - #13677

Merged
Frenzie merged 2 commits into
koreader:masterfrom
edoput:master
Sep 14, 2025
Merged

plugin handlers traceback#13677
Frenzie merged 2 commits into
koreader:masterfrom
edoput:master

Conversation

@edoput

@edoput edoput commented Apr 24, 2025

Copy link
Copy Markdown
Contributor

Errors in plugins are logged but they are not actionable. As a plugin developer I expect some help from koreader.

This patch provides a way to get a lua stack trace when a plugin event handler raises an error. This is achieved by wrapping each event handler with xpcall in place of pcall. This lua builint allows executing a error handler before the stack is unwound. The traceback we get is then from the execution in the plugin context.

The following is an example of where I am stuck. These are the last two errors I got. I am not sure if I have uncovered known errors or I am doing something wrong in the wrapping.

I made this patch by unpacking the appimage but I will transition to kodev soon to test more.

edoput@nibbler ~/r/koreader [SIGINT]> ./AppRun
---------------------------------------------
                launching...
  _  _____  ____                _
 | |/ / _ \|  _ \ ___  __ _  __| | ___ _ __
 | ' / | | | |_) / _ \/ _` |/ _` |/ _ \ '__|
 | . \ |_| |  _ <  __/ (_| | (_| |  __/ |
 |_|\_\___/|_| \_\___|\__,_|\__,_|\___|_|

 It's a scroll... It's a codex... It's KOReader!

 [*] Current time: 04/24/25-19:04:43
has monolibtic? no (libs/libkoreader-monolibtic.so: cannot open shared object file: No such file or directory)
lib_search_path: libs/?
lib_basic_format: lib%s.so
lib_version_format: lib%s.so.%s
 [*] Version: v2025.04

ffi.load: rt.so.1 (RTLD_GLOBAL)
ffi.findlib: utf8proc [3]
ffi.load: libs/libutf8proc.so.3
ffi.findlib: blitbuffer
ffi.load: libs/libblitbuffer.so
ffi.findlib: SDL2-2.0 [0]
ffi.load: libs/libSDL2-2.0.so.0
Starting SDL in /home/edoput/repo/koreader/
ffi.findlib: lodepng
ffi.load: libs/liblodepng.so
04/24/25-19:04:43 INFO  initializing for device Emulator 
04/24/25-19:04:43 INFO  framebuffer resolution: {
  h = 1401,
  w = 2560
} --[[table: 0x7fbd9350e678]] 
ffi.findlib: wrap-mupdf
ffi.load: libs/libwrap-mupdf.so
ffi.findlib: sqlite3 [0]
ffi.load: libs/libsqlite3.so.0
ffi.findlib: freetype [6]
ffi.load: libs/libfreetype.so.6
ffi.findlib: harfbuzz [0]
ffi.load: libs/libharfbuzz.so.0
ffi.findlib: zstd [1]
ffi.load: libs/libzstd.so.1
04/24/25-19:04:43 INFO  Looking for plugins in directory: plugins 
04/24/25-19:04:43 ERROR An error occurred while executing a handler:
plugins/profiles.koplugin/main.lua:994: attempt to index field 'autoexec' (a nil value)
profiles:onStart
stack traceback:
	plugins/profiles.koplugin/main.lua:994: in function 'executeAutoExecEvent'
	plugins/profiles.koplugin/main.lua:929: in function <plugins/profiles.koplugin/main.lua:927>
	[C]: in function 'xpcall'
	frontend/pluginloader.lua:78: in function 'onStart'
	plugins/profiles.koplugin/main.lua:34: in function 'init'
	frontend/ui/widget/widget.lua:46: in function <frontend/ui/widget/widget.lua:40>
	[C]: in function 'pcall'
	frontend/pluginloader.lua:293: in function 'createPluginInstance'
	frontend/apps/filemanager/filemanager.lua:412: in function 'init'
	frontend/ui/widget/widget.lua:46: in function 'new'
	frontend/apps/filemanager/filemanager.lua:1272: in function 'showFiles'
	./reader.lua:270: in main chunk
	[C]: at 0x563b0891c260 
04/24/25-19:04:44 ERROR An error occurred while executing a handler:
plugins/profiles.koplugin/main.lua:957: attempt to index field 'autoexec' (a nil value)
profiles:onPathChanged
stack traceback:
	plugins/profiles.koplugin/main.lua:957: in function <plugins/profiles.koplugin/main.lua:955>
	[C]: in function 'xpcall'
	frontend/pluginloader.lua:78: in function 'handleEvent'
	frontend/ui/widget/container/widgetcontainer.lua:83: in function 'propagateEvent'
	frontend/ui/widget/container/widgetcontainer.lua:101: in function 'handleEvent'
	frontend/apps/filemanager/filemanager.lua:426: in function 'init'
	frontend/ui/widget/widget.lua:46: in function 'new'
	frontend/apps/filemanager/filemanager.lua:1272: in function 'showFiles'
	./reader.lua:270: in main chunk
	[C]: at 0x563b0891c260 
04/24/25-19:08:43 INFO  Preparing to quit UIManager 
04/24/25-19:08:43 INFO  UIManager: No dialogs left to show 
04/24/25-19:08:43 INFO  Tearing down UIManager with exit code: 0 

This change is Reviewable

@mergen3107

Copy link
Copy Markdown
Contributor

plugins/profiles.koplugin/main.lua:994: attempt to index field 'autoexec' (a nil value)

Maybe @hius07 can help?

@edoput
edoput force-pushed the master branch 2 times, most recently from 02e89cd to 142d42d Compare April 24, 2025 17:50
Comment thread frontend/pluginloader.lua Outdated
-- and the second is the call method of HandlerSandbox.
logger.err("An error occurred while executing a handler:\n" ..err .. "\n" .. debug.traceback(self.context.name .. ":" .. self.fname, 2))
end
local ok, re = xpcall(self.f, traceback, self.context, unpack(arg)) -- unpack arg table into arguments

@NiLuJe NiLuJe Apr 24, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Anything wrong with keeping a clean variadic ... forward?

As is, this breaks if there are nils in the arguments (unpack can help with that, but only when using the three-argument form, where you pass the start & end indices, c.f., frontend/ui/widget/eventlistener.lua).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was not aware of the variadic forwarding. Is this a LuaJIT extension? I'll update to use that.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's just Lua. :-)

@edoput

edoput commented Apr 24, 2025

Copy link
Copy Markdown
Contributor Author

Running with kodev actually surfaces more errors.

Most are attempt to index field 'P' (a nil value).
One is attempt to compare nil with number

I'm not sure why these were not reported when using pcall instead.

The most likely culprit is my attempt at backward compatibility. In local ok, re = xpcall(self.f, traceback, self.context, ...) the argument self.context is passed as the plugin module in which the handler is defined.

The plugins I saw define handlers as methods of the module table and therefore I need to pass the module table itself as first argument by referencing self.context.

Is there a way to attach a debugger to koreader as is?

Here the additional tracebacks from the various plugins.

edoput@nibbler ~/r/koreader-clone (master) [SIGINT]> ./kodev run
make TARGET= KODEBUG=1 VERBOSE= RWRAP=EMULATE_READER_W=540\ EMULATE_READER_H=720\ EMULATE_READER_DPI= RARGS=-d run
fatal: No names found, cannot describe anything.
▸  0% | Building 'crengine'
ninja: no work to do.
▸ 50% | Building 'koreader'
ninja: no work to do.
▸100% | Installing 'koreader'
[*] Install front spec only for the emulator
[*] Install update once marker
[*] Install plugins
[*] Install resources
[*] Install data files
---------------------------------------------
                launching...
  _  _____  ____                _
 | |/ / _ \|  _ \ ___  __ _  __| | ___ _ __
 | ' / | | | |_) / _ \/ _` |/ _` |/ _ \ '__|
 | . \ |_| |  _ <  __/ (_| | (_| |  __/ |
 |_|\_\___/|_| \_\___|\__,_|\__,_|\___|_|

 It's a scroll... It's a codex... It's KOReader!

 [*] Current time: 04/24/25-21:22:39
has monolibtic? no (libs/libkoreader-monolibtic.so: cannot open shared object file: No such file or directory)
...
04/24/25-21:22:40 ERROR An error occurred while executing a handler:
plugins/profiles.koplugin/main.lua:994: attempt to index field 'autoexec' (a nil value)
profiles:onStart
stack traceback:
	plugins/profiles.koplugin/main.lua:994: in function 'executeAutoExecEvent'
	plugins/profiles.koplugin/main.lua:929: in function <plugins/profiles.koplugin/main.lua:927>
	[C]: in function 'xpcall'
	frontend/pluginloader.lua:77: in function 'onStart'
	plugins/profiles.koplugin/main.lua:34: in function 'init'
	frontend/ui/widget/widget.lua:46: in function <frontend/ui/widget/widget.lua:40>
	[C]: in function 'pcall'
	frontend/pluginloader.lua:284: in function 'createPluginInstance'
	frontend/apps/reader/readerui.lua:440: in function 'init'
	frontend/ui/widget/widget.lua:46: in function 'new'
	frontend/apps/reader/readerui.lua:713: in function 'doShowReader'
	frontend/apps/reader/readerui.lua:666: in function <frontend/apps/reader/readerui.lua:665> 
...
04/24/25-21:22:41 ERROR An error occurred while executing a handler:
plugins/autodim.koplugin/main.lua:165: attempt to compare nil with number
autodim:onPageUpdate
stack traceback:
	plugins/autodim.koplugin/main.lua:165: in function '_schedule_autodim_task'
	plugins/autodim.koplugin/main.lua:196: in function <plugins/autodim.koplugin/main.lua:191>
	[C]: in function 'xpcall'
	frontend/pluginloader.lua:77: in function 'handleEvent'
	frontend/ui/widget/container/widgetcontainer.lua:83: in function 'propagateEvent'
	frontend/ui/widget/container/widgetcontainer.lua:101: in function 'handleEvent'
	frontend/apps/reader/modules/readerrolling.lua:1192: in function '_gotoPage'
	frontend/apps/reader/modules/readerrolling.lua:1201: in function '_gotoXPointer'
	frontend/apps/reader/modules/readerrolling.lua:206: in function 'setupXpointer'
	frontend/apps/reader/modules/readerrolling.lua:349: in function 'handleEvent'
	frontend/ui/widget/container/widgetcontainer.lua:83: in function 'propagateEvent'
	frontend/ui/widget/container/widgetcontainer.lua:101: in function 'handleEvent'
	frontend/apps/reader/readerui.lua:491: in function 'init'
	frontend/ui/widget/widget.lua:46: in function 'new'
	frontend/apps/reader/readerui.lua:713: in function 'doShowReader'
	frontend/apps/reader/readerui.lua:666: in function <frontend/apps/reader/readerui.lua:665> 
...
04/24/25-21:22:41 ERROR An error occurred while executing a handler:
plugins/statistics.koplugin/main.lua:221: attempt to index field 'settings' (a nil value)
statistics:onPageUpdate
stack traceback:
	plugins/statistics.koplugin/main.lua:221: in function 'isEnabledAndNotFrozen'
	plugins/statistics.koplugin/main.lua:2670: in function <plugins/statistics.koplugin/main.lua:2669>
	[C]: in function 'xpcall'
	frontend/pluginloader.lua:77: in function 'handleEvent'
	frontend/ui/widget/container/widgetcontainer.lua:83: in function 'propagateEvent'
	frontend/ui/widget/container/widgetcontainer.lua:101: in function 'handleEvent'
	frontend/apps/reader/modules/readerrolling.lua:1192: in function '_gotoPage'
	frontend/apps/reader/modules/readerrolling.lua:1201: in function '_gotoXPointer'
	frontend/apps/reader/modules/readerrolling.lua:206: in function 'setupXpointer'
	frontend/apps/reader/modules/readerrolling.lua:349: in function 'handleEvent'
	frontend/ui/widget/container/widgetcontainer.lua:83: in function 'propagateEvent'
	frontend/ui/widget/container/widgetcontainer.lua:101: in function 'handleEvent'
	frontend/apps/reader/readerui.lua:491: in function 'init'
	frontend/ui/widget/widget.lua:46: in function 'new'
	frontend/apps/reader/readerui.lua:713: in function 'doShowReader'
	frontend/apps/reader/readerui.lua:666: in function <frontend/apps/reader/readerui.lua:665> 
...
04/24/25-21:22:41 ERROR An error occurred while executing a handler:
plugins/kosync.koplugin/main.lua:172: attempt to index field 'settings' (a nil value)
kosync:onReaderReady
stack traceback:
	plugins/kosync.koplugin/main.lua:172: in function <plugins/kosync.koplugin/main.lua:171>
	[C]: in function 'xpcall'
	frontend/pluginloader.lua:77: in function 'handleEvent'
	frontend/ui/widget/container/widgetcontainer.lua:83: in function 'propagateEvent'
	frontend/ui/widget/container/widgetcontainer.lua:101: in function 'handleEvent'
	frontend/apps/reader/readerui.lua:491: in function 'init'
	frontend/ui/widget/widget.lua:46: in function 'new'
	frontend/apps/reader/readerui.lua:713: in function 'doShowReader'
	frontend/apps/reader/readerui.lua:666: in function <frontend/apps/reader/readerui.lua:665> 
...
04/24/25-21:22:41 ERROR An error occurred while executing a handler:
plugins/perceptionexpander.koplugin/main.lua:98: attempt to index field 'ui' (a nil value)
perceptionexpander:onReaderReady
stack traceback:
	plugins/perceptionexpander.koplugin/main.lua:98: in function <plugins/perceptionexpander.koplugin/main.lua:97>
	[C]: in function 'xpcall'
	frontend/pluginloader.lua:77: in function 'handleEvent'
	frontend/ui/widget/container/widgetcontainer.lua:83: in function 'propagateEvent'
	frontend/ui/widget/container/widgetcontainer.lua:101: in function 'handleEvent'
	frontend/apps/reader/readerui.lua:491: in function 'init'
	frontend/ui/widget/widget.lua:46: in function 'new'
	frontend/apps/reader/readerui.lua:713: in function 'doShowReader'
	frontend/apps/reader/readerui.lua:666: in function <frontend/apps/reader/readerui.lua:665> 
...
04/24/25-21:22:41 ERROR An error occurred while executing a handler:
plugins/profiles.koplugin/main.lua:980: attempt to index field 'ui' (a nil value)
profiles:onReaderReady
stack traceback:
	plugins/profiles.koplugin/main.lua:980: in function <plugins/profiles.koplugin/main.lua:979>
	[C]: in function 'xpcall'
	frontend/pluginloader.lua:77: in function 'handleEvent'
	frontend/ui/widget/container/widgetcontainer.lua:83: in function 'propagateEvent'
	frontend/ui/widget/container/widgetcontainer.lua:101: in function 'handleEvent'
	frontend/apps/reader/readerui.lua:491: in function 'init'
	frontend/ui/widget/widget.lua:46: in function 'new'
	frontend/apps/reader/readerui.lua:713: in function 'doShowReader'
	frontend/apps/reader/readerui.lua:666: in function <frontend/apps/reader/readerui.lua:665> 
...
04/24/25-21:22:41 ERROR An error occurred while executing a handler:
plugins/statistics.koplugin/main.lua:2843: attempt to index field 'settings' (a nil value)
statistics:onReaderReady
stack traceback:
	plugins/statistics.koplugin/main.lua:2843: in function <plugins/statistics.koplugin/main.lua:2842>
	[C]: in function 'xpcall'
	frontend/pluginloader.lua:77: in function 'handleEvent'
	frontend/ui/widget/container/widgetcontainer.lua:83: in function 'propagateEvent'
	frontend/ui/widget/container/widgetcontainer.lua:101: in function 'handleEvent'
	frontend/apps/reader/readerui.lua:491: in function 'init'
	frontend/ui/widget/widget.lua:46: in function 'new'
	frontend/apps/reader/readerui.lua:713: in function 'doShowReader'
	frontend/apps/reader/readerui.lua:666: in function <frontend/apps/reader/readerui.lua:665> 
...
04/24/25-21:22:41 ERROR An error occurred while executing a handler:
plugins/autodim.koplugin/main.lua:165: attempt to compare nil with number
autodim:onPageUpdate
stack traceback:
	plugins/autodim.koplugin/main.lua:165: in function '_schedule_autodim_task'
	plugins/autodim.koplugin/main.lua:196: in function <plugins/autodim.koplugin/main.lua:191>
	[C]: in function 'xpcall'
	frontend/pluginloader.lua:77: in function 'handleEvent'
	frontend/ui/widget/container/widgetcontainer.lua:83: in function 'propagateEvent'
	frontend/ui/widget/container/widgetcontainer.lua:101: in function 'handleEvent'
	frontend/apps/reader/modules/readerrolling.lua:1086: in function 'onRedrawCurrentView'
	frontend/apps/reader/modules/readerrolling.lua:107: in function 'v'
	frontend/apps/reader/readerui.lua:494: in function 'init'
	frontend/ui/widget/widget.lua:46: in function 'new'
	frontend/apps/reader/readerui.lua:713: in function 'doShowReader'
	frontend/apps/reader/readerui.lua:666: in function <frontend/apps/reader/readerui.lua:665> 
...
04/24/25-21:22:41 ERROR An error occurred while executing a handler:
plugins/statistics.koplugin/main.lua:221: attempt to index field 'settings' (a nil value)
statistics:onPageUpdate
stack traceback:
	plugins/statistics.koplugin/main.lua:221: in function 'isEnabledAndNotFrozen'
	plugins/statistics.koplugin/main.lua:2670: in function <plugins/statistics.koplugin/main.lua:2669>
	[C]: in function 'xpcall'
	frontend/pluginloader.lua:77: in function 'handleEvent'
	frontend/ui/widget/container/widgetcontainer.lua:83: in function 'propagateEvent'
	frontend/ui/widget/container/widgetcontainer.lua:101: in function 'handleEvent'
	frontend/apps/reader/modules/readerrolling.lua:1086: in function 'onRedrawCurrentView'
	frontend/apps/reader/modules/readerrolling.lua:107: in function 'v'
	frontend/apps/reader/readerui.lua:494: in function 'init'
	frontend/ui/widget/widget.lua:46: in function 'new'
	frontend/apps/reader/readerui.lua:713: in function 'doShowReader'
	frontend/apps/reader/readerui.lua:666: in function <frontend/apps/reader/readerui.lua:665> 

@Frenzie

Frenzie commented Apr 24, 2025

Copy link
Copy Markdown
Member

@poire-z

poire-z commented Apr 24, 2025

Copy link
Copy Markdown
Contributor

I'm not sure I understand what these stacktraces you get are.
We don't get them on normal koreader.
You mean you get them after your wrapping (and you didn't before), so there's something wrong with your wrapping ? Or you mean you get them and you're just hoping your change will give you more details about these existing errors ?

For what's it's worth, I have used xpcall in frontend/ui/trapper.lua and plugins/httpinspector.koplugin/main.lua.
Also see how much performance loss there could be with xpcall vs. pcall, plugins may get lots of events, we don't want to slow down the things when it's not needed, so if it's slower, it could default to off and be toggled from our Developper options menu.

@edoput

edoput commented Apr 24, 2025

Copy link
Copy Markdown
Contributor Author

I'm not sure I understand what these stacktraces you get are. We don't get them on normal koreader. You mean you get them after your wrapping (and you didn't before), so there's something wrong with your wrapping ? Or you mean you get them and you're just hoping your change will give you more details about these existing errors ?

I get them after wrapping and there was no error before. I will try running with the debugger attached so I can see if it's my fault.

For what's it's worth, I have used xpcall in frontend/ui/trapper.lua and plugins/httpinspector.koplugin/main.lua. Also see how much performance loss there could be with xpcall vs. pcall, plugins may get lots of events, we don't want to slow down the things when it's not needed, so if it's slower, it could default to off and be toggled from our Developper options menu.

Thanks for the pointers.

@poire-z

poire-z commented Apr 24, 2025

Copy link
Copy Markdown
Contributor

From what I see in the stacktraces, all the mess happens when indexing self (self.autoexec, self.ui, self.settings), so it feels like you're not passing self as the first arg of a method (: used).
Either self is not passed, or there is another thing before and self is arg#2. Or your HandlerSandbox:call(...) (with : gets your HandlerSandbox instanced passed as arg#1...

@edoput

edoput commented Apr 24, 2025

Copy link
Copy Markdown
Contributor Author

From what I see in the stacktraces, all the mess happens when indexing self (self.autoexec, self.ui, self.settings), so it feels like you're not passing self as the first arg of a method (: used). Either self is not passed, or there is another thing before and self is arg#2. Or your HandlerSandbox:call(...) (with : gets your HandlerSandbox instanced passed as arg#1...

In a previous revision I would not pass the module as self but this is what happens now. self.context in this case is the plugin table/module and is passed as the first argument to the handler, the rest of the arguments is forwarded.

@edoput

edoput commented Apr 24, 2025

Copy link
Copy Markdown
Contributor Author

Poking around with the debugger I can now say the following: the handlers may be fired before the plugin is initialized.

On my laptop I can reliably trigger handlers for the AutoDim plugin by clicking around, this happens before the AutoDim:init is called meaning that I get the plugins/autodim.koplugin/main.lua:165: attempt to compare nil with number because self.autodim_starttime_m is nil until initialized in AutoDim:init.

The same explanation goes for the rest of the errors that I see (I hope?). I'm not sure what the lifecycle of the plugin is, e.g. when/where init is called.

@NiLuJe

NiLuJe commented Apr 24, 2025

Copy link
Copy Markdown
Member

init is called via new, which in case of Plugins, means via PluginLoader:createPluginInstance.

That said, the handlers should only actually get reached by Events (as, as such, fired) after they've been "tied" to a top-level widget. In case of plugins, that's specifically either ReaderUI or FileManager, via registerModule, and that happens to be the exact same thing that calls createPluginInstance, sooooo, that sounds weird.

Unless you've got stale instances of previous ReaderUI or FM instances (despite the following, somehow?), and its the teardown of ReaderUI/FM (that has a framework in place to orphan plugin instances, via PluginLoader:finalize during their own teardown) that's fucked somewhere.

@NiLuJe

NiLuJe commented Apr 24, 2025

Copy link
Copy Markdown
Member

We take great pains to avoid this sort of shenanigans happening (and both FM/RD themselves should shout very loudly if they seem to think it is happening on init), but it is fairly easy to break, and it has been horribly broken in the past, so, check which instance of the main "app" the broken stuff is actually tied to (i.e., what the ui member of a Plugin instance points to).

(And, for that matter, which instances of Plugins themselves are actually broken).

@edoput

edoput commented Apr 25, 2025

Copy link
Copy Markdown
Contributor Author

Poking around with the debugger I can now say the following: the handlers may be fired before the plugin is initialized.

On my laptop I can reliably trigger handlers for the AutoDim plugin by clicking around, this happens before the AutoDim:init is called meaning that I get the plugins/autodim.koplugin/main.lua:165: attempt to compare nil with number because self.autodim_starttime_m is nil until initialized in AutoDim:init.

The same explanation goes for the rest of the errors that I see (I hope?). I'm not sure what the lifecycle of the plugin is, e.g. when/where init is called.

I was passing the plugin module as self in place of the plugin module instance. Doh!

I am now happy with the outcome, I have stacktraces on event handlers. I think it should be easy to use xpcall everywhere in this file with the same approach and get stacktraces at load time too.

I will take a look at frontend/ui/trapper.lua and plugins/httpinspector.koplugin/main.lua to see if I can steal something wrt choosing xpcall or pcall at runtime

@edoput

edoput commented Apr 26, 2025

Copy link
Copy Markdown
Contributor Author

Is there a way to run the checks from the CI locally?

@benoit-pierre

Copy link
Copy Markdown
Member

Which parts?

For luacheck: luacheck -q {reader,setupkoenv,datastorage}.lua frontend plugins spec (Cf. https://github.com/koreader/koreader/blob/master/.ci/check.sh#L42)

@edoput

edoput commented Apr 26, 2025

Copy link
Copy Markdown
Contributor Author

Which parts?

For luacheck: luacheck -q {reader,setupkoenv,datastorage}.lua frontend plugins spec (Cf. https://github.com/koreader/koreader/blob/master/.ci/check.sh#L42)

Thanks. I think this will be enough. Most of the CI builds are because I failed to retab the file so this should fix the rest.

Comment thread frontend/pluginloader.lua Outdated
return setmetatable(t, HandlerSandbox.mt)
end

function HandlerSandbox:call(module,...)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

über-nit: Missing space after the comma ;).

Comment thread frontend/pluginloader.lua Outdated
-- self parameter of the handlers
local ok, re
if self.log_stacktrace then
local traceback = function (err)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

über-nit bis: no space between the function keyword and the parens

@edoput

edoput commented Apr 26, 2025

Copy link
Copy Markdown
Contributor Author

For what's it's worth, I have used xpcall in frontend/ui/trapper.lua and plugins/httpinspector.koplugin/main.lua.
Also see how much performance loss there could be with xpcall vs. pcall, plugins may get lots of events, we don't want to slow down the things when it's not needed, so if it's slower, it could default to off and be toggled from our Developper options menu.

Do you want the toggling to be available at runtime? Right now it's done at load-time but I don't see a developer options menu. If you can point me towards it I can add the toggling at runtime.

@poire-z

poire-z commented Apr 26, 2025

Copy link
Copy Markdown
Contributor

I don't see a developer options menu. If you can point me towards it I can add the toggling at runtime.

It's available only in File browser (not in Reader).

self.menu_items.developer_options = {
text = _("Developer options"),
sub_item_table = {
{
text = _("Clear caches"),
callback = function()
UIManager:show(ConfirmBox:new{
text = _("Clear the cache folder?"),
ok_callback = function()
local DataStorage = require("datastorage")
local cachedir = DataStorage:getDataDir() .. "/cache"
if lfs.attributes(cachedir, "mode") == "directory" then
ffiUtil.purgeDir(cachedir)
end
lfs.mkdir(cachedir)
-- Also remove from the Cache object references to the cache files we've just deleted
local Cache = require("cache")
Cache.cached = {}
UIManager:askForRestart(_("Caches cleared. Please restart KOReader."))
end,
})
end,
},
{
text = _("Enable debug logging"),
checked_func = function()
return G_reader_settings:isTrue("debug")
end,
callback = function()
G_reader_settings:flipNilOrFalse("debug")
if G_reader_settings:isTrue("debug") then
dbg:turnOn()
else
dbg:setVerbose(false)
dbg:turnOff()
G_reader_settings:makeFalse("debug_verbose")
end
end,
},
{
text = _("Enable verbose debug logging"),
enabled_func = function()
return G_reader_settings:isTrue("debug")
end,
checked_func = function()
return G_reader_settings:isTrue("debug_verbose")
end,
callback = function()
G_reader_settings:flipNilOrFalse("debug_verbose")
if G_reader_settings:isTrue("debug_verbose") then
dbg:setVerbose(true)
else
dbg:setVerbose(false)
end
end,
},
},
}
if Device:isKobo() and not Device:isSunxi() and not Device:hasColorScreen() then
table.insert(self.menu_items.developer_options.sub_item_table, {
text = _("Disable forced 8-bit pixel depth"),
checked_func = function()
return G_reader_settings:isTrue("dev_startup_no_fbdepth")
end,
callback = function()
G_reader_settings:flipNilOrFalse("dev_startup_no_fbdepth")
UIManager:askForRestart()
end,
})
end
--- @note Currently, only Kobo, rM & PB have a fancy crash display (#5328)
if Device:isKobo() or Device:isRemarkable() or Device:isPocketBook() then
table.insert(self.menu_items.developer_options.sub_item_table, {
text = _("Always abort on crash"),
checked_func = function()
return G_reader_settings:isTrue("dev_abort_on_crash")
end,
callback = function()
G_reader_settings:flipNilOrFalse("dev_abort_on_crash")
UIManager:askForRestart()
end,
})
end
local Blitbuffer = require("ffi/blitbuffer")
table.insert(self.menu_items.developer_options.sub_item_table, {
text = _("Disable C blitter"),
enabled_func = function()
return Blitbuffer.has_cblitbuffer
end,
checked_func = function()
return G_reader_settings:isTrue("dev_no_c_blitter")
end,
callback = function()
G_reader_settings:flipNilOrFalse("dev_no_c_blitter")
Blitbuffer:enableCBB(G_reader_settings:nilOrFalse("dev_no_c_blitter"))
end,
})
if Device:hasEinkScreen() and Device:canHWDither() then
table.insert(self.menu_items.developer_options.sub_item_table, {
text = _("Disable HW dithering"),
checked_func = function()
return not Device.screen.hw_dithering
end,
callback = function()
Device.screen:toggleHWDithering()
G_reader_settings:saveSetting("dev_no_hw_dither", not Device.screen.hw_dithering)
-- Make sure SW dithering gets disabled when we enable HW dithering
if Device.screen.hw_dithering and Device.screen.sw_dithering then
G_reader_settings:makeTrue("dev_no_sw_dither")
Device.screen:toggleSWDithering(false)
end
UIManager:setDirty("all", "full")
end,
})
end
if Device:hasEinkScreen() then
table.insert(self.menu_items.developer_options.sub_item_table, {
text = _("Disable SW dithering"),
enabled_func = function()
return Device.screen.fb_bpp == 8
end,
checked_func = function()
return not Device.screen.sw_dithering
end,
callback = function()
Device.screen:toggleSWDithering()
G_reader_settings:saveSetting("dev_no_sw_dither", not Device.screen.sw_dithering)
-- Make sure HW dithering gets disabled when we enable SW dithering
if Device.screen.hw_dithering and Device.screen.sw_dithering then
G_reader_settings:makeTrue("dev_no_hw_dither")
Device.screen:toggleHWDithering(false)
end
UIManager:setDirty("all", "full")
end,
})
end
if Device:isKobo() and Device:hasColorScreen() then
table.insert(self.menu_items.developer_options.sub_item_table, {
-- We default to a flag (G2) that slightly boosts saturation,
-- but it *is* a destructive process, so we want to allow disabling it.
-- @translators CFA is a technical term for the technology behind eInk's color panels. It stands for Color Film/Filter Array, leave the abbreviation alone ;).
text = _("Disable CFA post-processing"),
checked_func = function()
return G_reader_settings:isTrue("no_cfa_post_processing")
end,
callback = function()
G_reader_settings:flipNilOrFalse("no_cfa_post_processing")
UIManager:askForRestart()
end,
})
end
table.insert(self.menu_items.developer_options.sub_item_table, {
text = _("Anti-alias rounded corners"),
checked_func = function()
return G_reader_settings:nilOrTrue("anti_alias_ui")
end,
callback = function()
G_reader_settings:flipNilOrTrue("anti_alias_ui")
end,
})
--- @note: Currently, only Kobo implements this quirk
if Device:hasEinkScreen() and Device:isKobo() then
table.insert(self.menu_items.developer_options.sub_item_table, {
-- @translators Highly technical (ioctl is a Linux API call, the uppercase stuff is a constant). What's translatable is essentially only the action ("bypass") and the article.
text = _("Bypass the WAIT_FOR ioctls"),
checked_func = function()
local mxcfb_bypass_wait_for
if G_reader_settings:has("mxcfb_bypass_wait_for") then
mxcfb_bypass_wait_for = G_reader_settings:isTrue("mxcfb_bypass_wait_for")
else
mxcfb_bypass_wait_for = not Device:hasReliableMxcWaitFor()
end
return mxcfb_bypass_wait_for
end,
callback = function()
local mxcfb_bypass_wait_for
if G_reader_settings:has("mxcfb_bypass_wait_for") then
mxcfb_bypass_wait_for = G_reader_settings:isTrue("mxcfb_bypass_wait_for")
else
mxcfb_bypass_wait_for = not Device:hasReliableMxcWaitFor()
end
G_reader_settings:saveSetting("mxcfb_bypass_wait_for", not mxcfb_bypass_wait_for)
UIManager:askForRestart()
end,
})
end
--- @note: Intended to debug/investigate B288 quirks on PocketBook devices
if Device:hasEinkScreen() and Device:isPocketBook() then
table.insert(self.menu_items.developer_options.sub_item_table, {
-- @translators B288 is the codename of the CPU/chipset (SoC stands for 'System on Chip').
text = _("Ignore feature bans on B288 SoCs"),
enabled_func = function()
return Device:isB288SoC()
end,
checked_func = function()
return G_reader_settings:isTrue("pb_ignore_b288_quirks")
end,
callback = function()
G_reader_settings:flipNilOrFalse("pb_ignore_b288_quirks")
UIManager:askForRestart()
end,
})
end
if Device:isAndroid() then
table.insert(self.menu_items.developer_options.sub_item_table, {
text = _("Start compatibility test"),
callback = function()
Device:test()
end,
})
end
table.insert(self.menu_items.developer_options.sub_item_table, {
text = _("Disable enhanced UI text shaping (xtext)"),
checked_func = function()
return G_reader_settings:isFalse("use_xtext")
end,
callback = function()
G_reader_settings:flipNilOrTrue("use_xtext")
UIManager:askForRestart()
end,
})
table.insert(self.menu_items.developer_options.sub_item_table, {
text = _("UI layout mirroring and text direction"),
sub_item_table = {
{
text = _("Reverse UI layout mirroring"),
checked_func = function()
return G_reader_settings:isTrue("dev_reverse_ui_layout_mirroring")
end,
callback = function()
G_reader_settings:flipNilOrFalse("dev_reverse_ui_layout_mirroring")
UIManager:askForRestart()
end
},
{
text = _("Reverse UI text direction"),
checked_func = function()
return G_reader_settings:isTrue("dev_reverse_ui_text_direction")
end,
callback = function()
G_reader_settings:flipNilOrFalse("dev_reverse_ui_text_direction")
UIManager:askForRestart()
end
},
},
})
table.insert(self.menu_items.developer_options.sub_item_table, {
text_func = function()
if G_reader_settings:nilOrTrue("use_cre_call_cache")
and G_reader_settings:isTrue("use_cre_call_cache_log_stats") then
return _("Enable CRE call cache (with stats)")
end
return _("Enable CRE call cache")
end,
checked_func = function()
return G_reader_settings:nilOrTrue("use_cre_call_cache")
end,
callback = function()
G_reader_settings:flipNilOrTrue("use_cre_call_cache")
-- No need to show "This will take effect on next CRE book opening."
-- as this menu is only accessible from file browser
end,
hold_callback = function(touchmenu_instance)
G_reader_settings:flipNilOrFalse("use_cre_call_cache_log_stats")
touchmenu_instance:updateItems()
end,
})
table.insert(self.menu_items.developer_options.sub_item_table, {
text = _("Dump the fontlist cache"),
callback = function()
local FontList = require("fontlist")
FontList:dumpFontList()
end,
})
if Device:isKobo() and Device:canToggleChargingLED() then
table.insert(self.menu_items.developer_options.sub_item_table, {
-- @translators This is a debug option to help determine cases when standby failed to initiate properly. PM = power management.
text = _("Turn on the LED on PM entry failure"),
checked_func = function()
return G_reader_settings:isTrue("pm_debug_entry_failure")
end,
callback = function()
G_reader_settings:toggle("pm_debug_entry_failure")
end,
})
end

Do you want the toggling to be available at runtime? Right now it's done at load-time

Load-time is fine. When one is testing some plugin, he will restart koreader often enough :)
There are lots of UIManager:askForRestart() in there, as most are load-time.

@edoput

edoput commented Apr 29, 2025

Copy link
Copy Markdown
Contributor Author

I am done and happy with the result. I will submit a second PR for errors when loading plugins.

@edoput

edoput commented May 8, 2025

Copy link
Copy Markdown
Contributor Author

@NiLuJe any comment on the plugin handlers sandbox that I can address?

@NiLuJe

NiLuJe commented May 8, 2025

Copy link
Copy Markdown
Member

No time to review this seriously for a couple of weeks, sorry ;)..

@Frenzie
Frenzie merged commit 8da7774 into koreader:master Sep 14, 2025
@Frenzie Frenzie added this to the 2025.10 milestone Sep 14, 2025
0xstillb pushed a commit to 0xstillb/koreader-thai that referenced this pull request May 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants