2017-04-14 19:12:28 +00:00
local Device = require ( " device " )
2021-08-30 03:03:56 +00:00
local Dispatcher = require ( " dispatcher " )
2017-04-02 21:46:19 +00:00
local KeyValuePage = require ( " ui/widget/keyvaluepage " )
2022-06-27 19:16:51 +00:00
local Math = require ( " optmath " )
2017-04-02 21:46:19 +00:00
local UIManager = require ( " ui/uimanager " )
local WidgetContainer = require ( " ui/widget/container/widgetcontainer " )
2022-05-05 19:00:22 +00:00
local time = require ( " ui/time " )
2017-04-14 19:12:28 +00:00
local util = require ( " util " )
2017-04-02 21:46:19 +00:00
local _ = require ( " gettext " )
local SystemStat = {
2022-06-27 19:16:51 +00:00
start_time = time.realtime ( ) ,
start_monotonic_time = time.boottime_or_realtime_coarse ( ) ,
suspend_time = nil ,
resume_time = nil ,
2017-04-02 21:46:19 +00:00
wakeup_count = 0 ,
sleep_count = 0 ,
charge_count = 0 ,
discharge_count = 0 ,
}
2017-04-14 19:12:28 +00:00
function SystemStat : init ( )
2019-05-03 18:38:00 +00:00
if Device : isCervantes ( ) or Device : isPocketBook ( ) then
2017-04-14 19:12:28 +00:00
self.storage_filter = " mmcblk "
2019-05-03 18:38:00 +00:00
elseif Device : isKobo ( ) then
self.storage_filter = " /mnt/ "
2017-04-14 19:12:28 +00:00
elseif Device : isKindle ( ) then
self.storage_filter = " ' /mnt/us$' "
elseif Device : isSDL ( ) then
self.storage_filter = " /dev/sd "
end
2022-06-27 19:16:51 +00:00
-- Account for a start-up mid-charge
local powerd = Device : getPowerDevice ( )
if Device : hasAuxBattery ( ) and powerd : isAuxBatteryConnected ( ) then
if powerd : isAuxCharging ( ) and not powerd : isAuxCharged ( ) then
self.charge_count = self.charge_count + 1
end
else
if powerd : isCharging ( ) and not powerd : isCharged ( ) then
self.charge_count = self.charge_count + 1
end
end
2017-04-14 19:12:28 +00:00
end
function SystemStat : put ( p )
table.insert ( self.kv_pairs , p )
end
2021-02-20 19:15:43 +00:00
function SystemStat : putSeparator ( )
self.kv_pairs [ # self.kv_pairs ] . separator = true
end
2017-04-14 19:12:28 +00:00
function SystemStat : appendCounters ( )
2022-06-27 19:16:51 +00:00
self : put ( { _ ( " KOReader started at " ) , os.date ( " %c " , time.to_s ( self.start_time ) ) } )
if self.suspend_time then
self : put ( { _ ( " Last suspend time " ) , os.date ( " %c " , time.to_s ( self.suspend_time ) ) } )
2017-04-14 19:12:28 +00:00
end
2022-06-27 19:16:51 +00:00
if self.resume_time then
self : put ( { _ ( " Last resume time " ) , os.date ( " %c " , time.to_s ( self.resume_time ) ) } )
end
local uptime = time.boottime_or_realtime_coarse ( ) - self.start_monotonic_time
local suspend = 0
if Device : canSuspend ( ) then
suspend = Device.total_suspend_time
end
local standby = 0
if Device : canStandby ( ) then
standby = Device.total_standby_time
2017-04-14 19:12:28 +00:00
end
2022-04-10 15:54:32 +00:00
self : put ( { " " .. _ ( " Up time " ) ,
2022-06-27 19:16:51 +00:00
util.secondsToClockDuration ( " " , time.to_s ( uptime ) , false , true , true ) } )
if Device : canSuspend ( ) or Device : canStandby ( ) then
local awake = uptime - suspend - standby
self : put ( { " " .. _ ( " Time spent awake " ) ,
util.secondsToClockDuration ( " " , time.to_s ( awake ) , false , true , true )
.. " ( " .. Math.round ( ( awake / uptime ) * 100 ) .. " %) " } )
end
2022-03-29 20:59:10 +00:00
if Device : canSuspend ( ) then
2022-04-10 15:54:32 +00:00
self : put ( { " " .. _ ( " Time in suspend " ) ,
2022-06-27 19:16:51 +00:00
util.secondsToClockDuration ( " " , time.to_s ( suspend ) , false , true , true )
.. " ( " .. Math.round ( ( suspend / uptime ) * 100 ) .. " %) " } )
2022-03-29 20:59:10 +00:00
end
if Device : canStandby ( ) then
2022-04-10 15:54:32 +00:00
self : put ( { " " .. _ ( " Time in standby " ) ,
2022-06-27 19:16:51 +00:00
util.secondsToClockDuration ( " " , time.to_s ( standby ) , false , true , true )
.. " ( " .. Math.round ( ( standby / uptime ) * 100 ) .. " %) " } )
2022-03-29 20:59:10 +00:00
end
2017-04-14 19:12:28 +00:00
self : put ( { _ ( " Counters " ) , " " } )
self : put ( { _ ( " wake-ups " ) , self.wakeup_count } )
2019-08-24 07:25:38 +00:00
-- @translators The number of "sleeps", that is the number of times the device has entered standby. This could also be translated as a rendition of a phrase like "entered sleep".
2017-04-14 19:12:28 +00:00
self : put ( { _ ( " sleeps " ) , self.sleep_count } )
self : put ( { _ ( " charge cycles " ) , self.charge_count } )
self : put ( { _ ( " discharge cycles " ) , self.discharge_count } )
end
local function systemInfo ( )
local result = { }
do
local stat = io.open ( " /proc/stat " , " r " )
if stat ~= nil then
2021-01-16 03:41:46 +00:00
for line in stat : lines ( ) do
2017-04-14 19:12:28 +00:00
local t = util.splitToArray ( line , " " )
if # t >= 5 and string.lower ( t [ 1 ] ) == " cpu " then
local n1 , n2 , n3 , n4
n1 = tonumber ( t [ 2 ] )
n2 = tonumber ( t [ 3 ] )
n3 = tonumber ( t [ 4 ] )
n4 = tonumber ( t [ 5 ] )
if n1 ~= nil and n2 ~= nil and n3 ~= nil and n4 ~= nil then
result.cpu = {
user = n1 ,
nice = n2 ,
system = n3 ,
idle = n4 ,
total = n1 + n2 + n3 + n4
}
break
end
end
end
stat : close ( )
end
end
do
local meminfo = io.open ( " /proc/meminfo " , " r " )
if meminfo ~= nil then
result.memory = { }
2021-01-16 03:41:46 +00:00
for line in meminfo : lines ( ) do
2017-04-14 19:12:28 +00:00
local t = util.splitToArray ( line , " " )
if # t >= 2 then
if string.lower ( t [ 1 ] ) == " memtotal: " then
local n = tonumber ( t [ 2 ] )
if n ~= nil then
result.memory . total = n
end
elseif string.lower ( t [ 1 ] ) == " memfree: " then
local n = tonumber ( t [ 2 ] )
if n ~= nil then
result.memory . free = n
end
elseif string.lower ( t [ 1 ] ) == " memavailable: " then
local n = tonumber ( t [ 2 ] )
if n ~= nil then
result.memory . available = n
end
end
end
end
meminfo : close ( )
end
end
return result
end
function SystemStat : appendSystemInfo ( )
local stat = systemInfo ( )
if stat.cpu ~= nil then
self : put ( { _ ( " System information " ) , " " } )
2019-08-24 07:25:38 +00:00
-- @translators Ticks is a highly technical term. See https://superuser.com/a/101202 The correct translation is likely to simply be "ticks".
2017-04-14 19:12:28 +00:00
self : put ( { _ ( " Total ticks (million) " ) ,
string.format ( " %.2f " , stat.cpu . total / 1000000 ) } )
2019-08-24 07:25:38 +00:00
-- @translators Ticks is a highly technical term. See https://superuser.com/a/101202 The correct translation is likely to simply be "ticks".
2017-04-14 19:12:28 +00:00
self : put ( { _ ( " Idle ticks (million) " ) ,
string.format ( " %.2f " , stat.cpu . idle / 1000000 ) } )
self : put ( { _ ( " Processor usage % " ) ,
string.format ( " %.2f " , ( 1 - stat.cpu . idle / stat.cpu . total ) * 100 ) } )
end
if stat.memory ~= nil then
if stat.memory . total ~= nil then
self : put ( { _ ( " Total memory (MB) " ) ,
string.format ( " %.2f " , stat.memory . total / 1024 ) } )
end
if stat.memory . free ~= nil then
self : put ( { _ ( " Free memory (MB) " ) ,
string.format ( " %.2f " , stat.memory . free / 1024 ) } )
end
if stat.memory . available ~= nil then
self : put ( { _ ( " Available memory (MB) " ) ,
string.format ( " %.2f " , stat.memory . available / 1024 ) } )
end
end
end
function SystemStat : appendProcessInfo ( )
local stat = io.open ( " /proc/self/stat " , " r " )
if stat == nil then return end
2021-01-16 03:41:46 +00:00
local t = util.splitToArray ( stat : read ( " *line " ) , " " )
2017-04-14 19:12:28 +00:00
stat : close ( )
local n1 , n2
if # t == 0 then return end
self : put ( { _ ( " Process " ) , " " } )
self : put ( { _ ( " ID " ) , t [ 1 ] } )
if # t < 14 then return end
n1 = tonumber ( t [ 14 ] )
n2 = tonumber ( t [ 15 ] )
if n1 ~= nil then
if n2 ~= nil then
n1 = n1 + n2
end
local sys_stat = systemInfo ( )
if sys_stat.cpu ~= nil and sys_stat.cpu . total ~= nil then
self : put ( { _ ( " Processor usage % " ) ,
string.format ( " %.2f " , n1 / sys_stat.cpu . total * 100 ) } )
else
self : put ( { _ ( " Processor usage ticks (million) " ) , n1 / 1000000 } )
end
end
if # t < 20 then return end
n1 = tonumber ( t [ 20 ] )
if n1 ~= nil then
self : put ( { _ ( " Threads " ) , tostring ( n1 ) } )
end
if # t < 23 then return end
n1 = tonumber ( t [ 23 ] )
if n1 ~= nil then
self : put ( { _ ( " Virtual memory (MB) " ) , string.format ( " %.2f " , n1 / 1024 / 1024 ) } )
end
if # t < 24 then return end
n1 = tonumber ( t [ 24 ] )
if n1 ~= nil then
self : put ( { _ ( " RAM usage (MB) " ) , string.format ( " %.2f " , n1 / 256 ) } )
end
end
function SystemStat : appendStorageInfo ( )
if self.storage_filter == nil then return end
local std_out = io.popen (
" df -h | sed -r 's/ +/ /g' | grep " .. self.storage_filter ..
" | sed 's/ / \\ t/g' | cut -f 2,4,5,6 "
)
if not std_out then return end
self : put ( { _ ( " Storage information " ) , " " } )
2021-01-16 03:41:46 +00:00
for line in std_out : lines ( ) do
2017-04-14 19:12:28 +00:00
local t = util.splitToArray ( line , " \t " )
if # t ~= 4 then
self : put ( { _ ( " Unexpected " ) , line } )
else
self : put ( { _ ( " Mount point " ) , t [ 4 ] } )
self : put ( { _ ( " Available " ) , t [ 2 ] } )
self : put ( { _ ( " Total " ) , t [ 1 ] } )
self : put ( { _ ( " Used percentage " ) , t [ 3 ] } )
end
end
std_out : close ( )
end
2017-04-02 21:46:19 +00:00
function SystemStat : onSuspend ( )
2022-06-27 19:16:51 +00:00
self.suspend_time = time.realtime ( )
2017-04-02 21:46:19 +00:00
self.sleep_count = self.sleep_count + 1
end
function SystemStat : onResume ( )
2022-06-27 19:16:51 +00:00
self.resume_time = time.realtime ( )
2017-04-02 21:46:19 +00:00
self.wakeup_count = self.wakeup_count + 1
end
function SystemStat : onCharging ( )
self.charge_count = self.charge_count + 1
end
function SystemStat : onNotCharging ( )
self.discharge_count = self.discharge_count + 1
end
function SystemStat : showStatistics ( )
2017-04-14 19:12:28 +00:00
self.kv_pairs = { }
self : appendCounters ( )
2021-02-20 19:15:43 +00:00
self : putSeparator ( )
2017-04-14 19:12:28 +00:00
self : appendProcessInfo ( )
2021-02-20 19:15:43 +00:00
self : putSeparator ( )
2017-04-14 19:12:28 +00:00
self : appendStorageInfo ( )
2021-02-20 19:15:43 +00:00
self : putSeparator ( )
2017-04-14 19:12:28 +00:00
self : appendSystemInfo ( )
2017-04-02 21:46:19 +00:00
UIManager : show ( KeyValuePage : new {
title = _ ( " System statistics " ) ,
2017-04-14 19:12:28 +00:00
kv_pairs = self.kv_pairs ,
2017-04-02 21:46:19 +00:00
} )
end
2017-04-14 19:12:28 +00:00
SystemStat : init ( )
Clarify our OOP semantics across the codebase (#9586)
Basically:
* Use `extend` for class definitions
* Use `new` for object instantiations
That includes some minor code cleanups along the way:
* Updated `Widget`'s docs to make the semantics clearer.
* Removed `should_restrict_JIT` (it's been dead code since https://github.com/koreader/android-luajit-launcher/pull/283)
* Minor refactoring of LuaSettings/LuaData/LuaDefaults/DocSettings to behave (mostly, they are instantiated via `open` instead of `new`) like everything else and handle inheritance properly (i.e., DocSettings is now a proper LuaSettings subclass).
* Default to `WidgetContainer` instead of `InputContainer` for stuff that doesn't actually setup key/gesture events.
* Ditto for explicit `*Listener` only classes, make sure they're based on `EventListener` instead of something uselessly fancier.
* Unless absolutely necessary, do not store references in class objects, ever; only values. Instead, always store references in instances, to avoid both sneaky inheritance issues, and sneaky GC pinning of stale references.
* ReaderUI: Fix one such issue with its `active_widgets` array, with critical implications, as it essentially pinned *all* of ReaderUI's modules, including their reference to the `Document` instance (i.e., that was a big-ass leak).
* Terminal: Make sure the shell is killed on plugin teardown.
* InputText: Fix Home/End/Del physical keys to behave sensibly.
* InputContainer/WidgetContainer: If necessary, compute self.dimen at paintTo time (previously, only InputContainers did, which might have had something to do with random widgets unconcerned about input using it as a baseclass instead of WidgetContainer...).
* OverlapGroup: Compute self.dimen at *init* time, because for some reason it needs to do that, but do it directly in OverlapGroup instead of going through a weird WidgetContainer method that it was the sole user of.
* ReaderCropping: Under no circumstances should a Document instance member (here, self.bbox) risk being `nil`ed!
* Kobo: Minor code cleanups.
2022-10-06 00:14:48 +00:00
local SystemStatWidget = WidgetContainer : extend {
2017-04-02 21:46:19 +00:00
name = " systemstat " ,
}
2021-08-30 03:03:56 +00:00
function SystemStatWidget : onDispatcherRegisterActions ( )
Dispatcher : registerAction ( " system_statistics " , { category = " none " , event = " ShowSysStatistics " , title = _ ( " System statistics " ) , device = true , separator = true } )
end
2017-04-02 21:46:19 +00:00
function SystemStatWidget : init ( )
2021-08-30 03:03:56 +00:00
self : onDispatcherRegisterActions ( )
2017-04-02 21:46:19 +00:00
self.ui . menu : registerToMainMenu ( self )
end
function SystemStatWidget : addToMainMenu ( menu_items )
menu_items.system_statistics = {
text = _ ( " System statistics " ) ,
2018-09-04 21:55:58 +00:00
keep_menu_open = true ,
2017-04-02 21:46:19 +00:00
callback = function ( )
SystemStat : showStatistics ( )
end ,
}
end
2021-08-30 03:03:56 +00:00
function SystemStatWidget : onShowSysStatistics ( )
SystemStat : showStatistics ( )
end
2017-04-02 21:46:19 +00:00
function SystemStatWidget : onSuspend ( )
SystemStat : onSuspend ( )
end
function SystemStatWidget : onResume ( )
SystemStat : onResume ( )
end
function SystemStatWidget : onCharging ( )
SystemStat : onCharging ( )
end
function SystemStatWidget : onNotCharging ( )
SystemStat : onNotCharging ( )
end
return SystemStatWidget