From 857ee91d003637e1fa77bb5e20c144b68458b1e2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Nov 2022 00:15:03 +0000 Subject: [PATCH 001/106] Bump actions/checkout from 1 to 3 Bumps [actions/checkout](https://github.com/actions/checkout) from 1 to 3. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v1...v3) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/build_release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_release.yml b/.github/workflows/build_release.yml index 8c879156a..655853cc8 100644 --- a/.github/workflows/build_release.yml +++ b/.github/workflows/build_release.yml @@ -11,7 +11,7 @@ jobs: name: build runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v3 with: submodules: true From 09bf230e4fd313477e90178f87d6fcd757f01d1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= Date: Thu, 17 Nov 2022 17:21:43 +0100 Subject: [PATCH 002/106] CI: drop non existent labels from dependabot --- .github/dependabot.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index ca6a6ef73..5516fd63b 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -7,6 +7,3 @@ updates: directory: / schedule: interval: weekly - labels: - - 'type: dependencies' - - 'github-actions' From c5b0ccb9f9f03be1e74d470cc1908354a99d332f Mon Sep 17 00:00:00 2001 From: Benny Baumann Date: Thu, 17 Nov 2022 13:49:40 +0100 Subject: [PATCH 003/106] Allow to keep following when just changing display settings --- Action.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Action.c b/Action.c index 725e7841d..0f1c0c98a 100644 --- a/Action.c +++ b/Action.c @@ -228,12 +228,12 @@ static Htop_Reaction actionToggleRunningInContainer(State* st) { static Htop_Reaction actionToggleProgramPath(State* st) { st->settings->showProgramPath = !st->settings->showProgramPath; - return HTOP_REFRESH | HTOP_SAVE_SETTINGS; + return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING; } static Htop_Reaction actionToggleMergedCommand(State* st) { st->settings->showMergedCommand = !st->settings->showMergedCommand; - return HTOP_REFRESH | HTOP_SAVE_SETTINGS; + return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING | HTOP_UPDATE_PANELHDR; } static Htop_Reaction actionToggleTreeView(State* st) { From 1707642f5b2536dc5a30d54163f6ce6123754331 Mon Sep 17 00:00:00 2001 From: Benny Baumann Date: Thu, 17 Nov 2022 13:51:11 +0100 Subject: [PATCH 004/106] Refresh the settings->lastUpdate to force recalculation of cached display strings --- Action.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Action.c b/Action.c index 0f1c0c98a..59322e25e 100644 --- a/Action.c +++ b/Action.c @@ -213,26 +213,36 @@ static Htop_Reaction actionSortByTime(State* st) { static Htop_Reaction actionToggleKernelThreads(State* st) { st->settings->hideKernelThreads = !st->settings->hideKernelThreads; + st->settings->lastUpdate++; + return HTOP_RECALCULATE | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING; } static Htop_Reaction actionToggleUserlandThreads(State* st) { st->settings->hideUserlandThreads = !st->settings->hideUserlandThreads; + st->settings->lastUpdate++; + return HTOP_RECALCULATE | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING; } static Htop_Reaction actionToggleRunningInContainer(State* st) { st->settings->hideRunningInContainer = !st->settings->hideRunningInContainer; + st->settings->lastUpdate++; + return HTOP_RECALCULATE | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING; } static Htop_Reaction actionToggleProgramPath(State* st) { st->settings->showProgramPath = !st->settings->showProgramPath; + st->settings->lastUpdate++; + return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING; } static Htop_Reaction actionToggleMergedCommand(State* st) { st->settings->showMergedCommand = !st->settings->showMergedCommand; + st->settings->lastUpdate++; + return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING | HTOP_UPDATE_PANELHDR; } From cd7df1c871f0874e35e319ee11cab9e1ac0f6c6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= Date: Fri, 2 Dec 2022 14:47:39 +0100 Subject: [PATCH 005/106] CPUMeter: show frequency in text mode Closes: #1144 --- CPUMeter.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CPUMeter.c b/CPUMeter.c index f178804be..ba0059561 100644 --- a/CPUMeter.c +++ b/CPUMeter.c @@ -167,6 +167,18 @@ static void CPUMeter_display(const Object* cast, RichString* out) { } } + if (this->pl->settings->showCPUFrequency) { + char cpuFrequencyBuffer[10]; + double cpuFrequency = this->values[CPU_METER_FREQUENCY]; + if (isnan(cpuFrequency)) { + len = xSnprintf(cpuFrequencyBuffer, sizeof(cpuFrequencyBuffer), "N/A "); + } else { + len = xSnprintf(cpuFrequencyBuffer, sizeof(cpuFrequencyBuffer), "%4uMHz ", (unsigned)cpuFrequency); + } + RichString_appendAscii(out, CRT_colors[METER_TEXT], "freq: "); + RichString_appendnWide(out, CRT_colors[METER_VALUE], cpuFrequencyBuffer, len); + } + #ifdef BUILD_WITH_CPU_TEMP if (this->pl->settings->showCPUTemperature) { char cpuTemperatureBuffer[10]; From 21e2875570e6dc0499f571950532c0f7f33e524d Mon Sep 17 00:00:00 2001 From: Christian Hesse Date: Mon, 7 Nov 2022 11:57:21 +0100 Subject: [PATCH 006/106] DiskIOMeter: use define ONE_K ... instead of numerical value. It is defined in Process.h. --- DiskIOMeter.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DiskIOMeter.c b/DiskIOMeter.c index 12c87de61..925a2c41e 100644 --- a/DiskIOMeter.c +++ b/DiskIOMeter.c @@ -64,7 +64,7 @@ static void DiskIOMeter_updateValues(Meter* this) { if (data.totalBytesRead > cached_read_total) { diff = data.totalBytesRead - cached_read_total; - diff /= 1024; /* Meter_humanUnit() expects unit in kilo */ + diff /= ONE_K; /* convert to KiB/s */ cached_read_diff = (uint32_t)diff; } else { cached_read_diff = 0; @@ -73,7 +73,7 @@ static void DiskIOMeter_updateValues(Meter* this) { if (data.totalBytesWritten > cached_write_total) { diff = data.totalBytesWritten - cached_write_total; - diff /= 1024; /* Meter_humanUnit() expects unit in kilo */ + diff /= ONE_K; /* convert to KiB/s */ cached_write_diff = (uint32_t)diff; } else { cached_write_diff = 0; From 078a7657f2189895d2d5a8e931ca413841e5d5ed Mon Sep 17 00:00:00 2001 From: Christian Hesse Date: Mon, 7 Nov 2022 11:59:07 +0100 Subject: [PATCH 007/106] DiskIOMeter: convert to bytes per second ... do not show values per interval (which is 1.5 seconds by default). --- DiskIOMeter.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/DiskIOMeter.c b/DiskIOMeter.c index 925a2c41e..1adce8e01 100644 --- a/DiskIOMeter.c +++ b/DiskIOMeter.c @@ -64,6 +64,7 @@ static void DiskIOMeter_updateValues(Meter* this) { if (data.totalBytesRead > cached_read_total) { diff = data.totalBytesRead - cached_read_total; + diff = (1000 * diff) / passedTimeInMs; /* convert to B/s */ diff /= ONE_K; /* convert to KiB/s */ cached_read_diff = (uint32_t)diff; } else { @@ -73,6 +74,7 @@ static void DiskIOMeter_updateValues(Meter* this) { if (data.totalBytesWritten > cached_write_total) { diff = data.totalBytesWritten - cached_write_total; + diff = (1000 * diff) / passedTimeInMs; /* convert to B/s */ diff /= ONE_K; /* convert to KiB/s */ cached_write_diff = (uint32_t)diff; } else { From 5c27f21fa96c71b1bc9dc686aab8351cb307ecea Mon Sep 17 00:00:00 2001 From: Christian Hesse Date: Mon, 7 Nov 2022 12:03:28 +0100 Subject: [PATCH 008/106] DiskIOMeter: use complete units There were missing "iB/s" before. Fixes #1126 --- DiskIOMeter.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/DiskIOMeter.c b/DiskIOMeter.c index 1adce8e01..e4b4d9564 100644 --- a/DiskIOMeter.c +++ b/DiskIOMeter.c @@ -106,7 +106,7 @@ static void DiskIOMeter_updateValues(Meter* this) { char bufferRead[12], bufferWrite[12]; Meter_humanUnit(bufferRead, cached_read_diff, sizeof(bufferRead)); Meter_humanUnit(bufferWrite, cached_write_diff, sizeof(bufferWrite)); - snprintf(this->txtBuffer, sizeof(this->txtBuffer), "%sB %sB %.1f%%", bufferRead, bufferWrite, cached_utilisation_diff); + snprintf(this->txtBuffer, sizeof(this->txtBuffer), "%siB/s %siB/s %.1f%%", bufferRead, bufferWrite, cached_utilisation_diff); } static void DiskIOMeter_display(ATTR_UNUSED const Object* cast, RichString* out) { @@ -134,10 +134,12 @@ static void DiskIOMeter_display(ATTR_UNUSED const Object* cast, RichString* out) RichString_appendAscii(out, CRT_colors[METER_TEXT], " read: "); Meter_humanUnit(buffer, cached_read_diff, sizeof(buffer)); RichString_appendAscii(out, CRT_colors[METER_VALUE_IOREAD], buffer); + RichString_appendAscii(out, CRT_colors[METER_VALUE_IOREAD], "iB/s"); RichString_appendAscii(out, CRT_colors[METER_TEXT], " write: "); Meter_humanUnit(buffer, cached_write_diff, sizeof(buffer)); RichString_appendAscii(out, CRT_colors[METER_VALUE_IOWRITE], buffer); + RichString_appendAscii(out, CRT_colors[METER_VALUE_IOWRITE], "iB/s"); } const MeterClass DiskIOMeter_class = { From 2b31363685a89f0543d1163e1b180d1362bfd0e1 Mon Sep 17 00:00:00 2001 From: Christian Hesse Date: Wed, 16 Nov 2022 16:11:49 +0100 Subject: [PATCH 009/106] DiskIOMeter: indicate read and write in meter mode ... as this can not be distinguished currently. --- DiskIOMeter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DiskIOMeter.c b/DiskIOMeter.c index e4b4d9564..adab8f732 100644 --- a/DiskIOMeter.c +++ b/DiskIOMeter.c @@ -106,7 +106,7 @@ static void DiskIOMeter_updateValues(Meter* this) { char bufferRead[12], bufferWrite[12]; Meter_humanUnit(bufferRead, cached_read_diff, sizeof(bufferRead)); Meter_humanUnit(bufferWrite, cached_write_diff, sizeof(bufferWrite)); - snprintf(this->txtBuffer, sizeof(this->txtBuffer), "%siB/s %siB/s %.1f%%", bufferRead, bufferWrite, cached_utilisation_diff); + snprintf(this->txtBuffer, sizeof(this->txtBuffer), "r:%siB/s w:%siB/s %.1f%%", bufferRead, bufferWrite, cached_utilisation_diff); } static void DiskIOMeter_display(ATTR_UNUSED const Object* cast, RichString* out) { From 55b948c65021d697e04b1d27583d4302991d8837 Mon Sep 17 00:00:00 2001 From: Christian Hesse Date: Tue, 8 Nov 2022 11:07:08 +0100 Subject: [PATCH 010/106] NetworkIOMeter: swap calculation for more precision Convert to per second first, divide for kilo after. --- NetworkIOMeter.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NetworkIOMeter.c b/NetworkIOMeter.c index dd91b75ea..20520e790 100644 --- a/NetworkIOMeter.c +++ b/NetworkIOMeter.c @@ -59,8 +59,8 @@ static void NetworkIOMeter_updateValues(Meter* this) { if (data.bytesReceived > cached_rxb_total) { diff = data.bytesReceived - cached_rxb_total; - diff /= ONE_K; /* Meter_humanUnit() expects unit in kilo */ diff = (1000 * diff) / passedTimeInMs; /* convert to per second */ + diff /= ONE_K; /* convert to KiB/s */ cached_rxb_diff = (uint32_t)diff; } else { cached_rxb_diff = 0; @@ -77,8 +77,8 @@ static void NetworkIOMeter_updateValues(Meter* this) { if (data.bytesTransmitted > cached_txb_total) { diff = data.bytesTransmitted - cached_txb_total; - diff /= ONE_K; /* Meter_humanUnit() expects unit in kilo */ diff = (1000 * diff) / passedTimeInMs; /* convert to per second */ + diff /= ONE_K; /* convert to KiB/s */ cached_txb_diff = (uint32_t)diff; } else { cached_txb_diff = 0; From 1fa75515f1e5d15575ad9a4f740a9112f0016e1d Mon Sep 17 00:00:00 2001 From: Christian Hesse Date: Mon, 7 Nov 2022 22:03:01 +0100 Subject: [PATCH 011/106] NetworkIOMeter: convert to packets per second We should normalize the packets as well... --- NetworkIOMeter.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/NetworkIOMeter.c b/NetworkIOMeter.c index 20520e790..3cd731f70 100644 --- a/NetworkIOMeter.c +++ b/NetworkIOMeter.c @@ -59,7 +59,7 @@ static void NetworkIOMeter_updateValues(Meter* this) { if (data.bytesReceived > cached_rxb_total) { diff = data.bytesReceived - cached_rxb_total; - diff = (1000 * diff) / passedTimeInMs; /* convert to per second */ + diff = (1000 * diff) / passedTimeInMs; /* convert to B/s */ diff /= ONE_K; /* convert to KiB/s */ cached_rxb_diff = (uint32_t)diff; } else { @@ -69,6 +69,7 @@ static void NetworkIOMeter_updateValues(Meter* this) { if (data.packetsReceived > cached_rxp_total) { diff = data.packetsReceived - cached_rxp_total; + diff = (1000 * diff) / passedTimeInMs; /* convert to B/s */ cached_rxp_diff = (uint32_t)diff; } else { cached_rxp_diff = 0; @@ -77,7 +78,7 @@ static void NetworkIOMeter_updateValues(Meter* this) { if (data.bytesTransmitted > cached_txb_total) { diff = data.bytesTransmitted - cached_txb_total; - diff = (1000 * diff) / passedTimeInMs; /* convert to per second */ + diff = (1000 * diff) / passedTimeInMs; /* convert to B/s */ diff /= ONE_K; /* convert to KiB/s */ cached_txb_diff = (uint32_t)diff; } else { @@ -87,6 +88,7 @@ static void NetworkIOMeter_updateValues(Meter* this) { if (data.packetsTransmitted > cached_txp_total) { diff = data.packetsTransmitted - cached_txp_total; + diff = (1000 * diff) / passedTimeInMs; /* convert to B/s */ cached_txp_diff = (uint32_t)diff; } else { cached_txp_diff = 0; @@ -143,7 +145,7 @@ static void NetworkIOMeter_display(ATTR_UNUSED const Object* cast, RichString* o RichString_appendAscii(out, CRT_colors[METER_VALUE_IOWRITE], buffer); RichString_appendAscii(out, CRT_colors[METER_VALUE_IOWRITE], "iB/s"); - len = xSnprintf(buffer, sizeof(buffer), " (%u/%u packets) ", cached_rxp_diff, cached_txp_diff); + len = xSnprintf(buffer, sizeof(buffer), " (%u/%u pkts/s) ", cached_rxp_diff, cached_txp_diff); RichString_appendnAscii(out, CRT_colors[METER_TEXT], buffer, len); } From 424b45ee277fb4a3f035bc3a26fca75b3f32ede9 Mon Sep 17 00:00:00 2001 From: Christian Hesse Date: Wed, 16 Nov 2022 16:16:19 +0100 Subject: [PATCH 012/106] NetworkIOMeter: show packet rate in meter mode Currently we have byte rate only. --- NetworkIOMeter.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/NetworkIOMeter.c b/NetworkIOMeter.c index 3cd731f70..d41eafa89 100644 --- a/NetworkIOMeter.c +++ b/NetworkIOMeter.c @@ -114,7 +114,8 @@ static void NetworkIOMeter_updateValues(Meter* this) { char bufferBytesReceived[12], bufferBytesTransmitted[12]; Meter_humanUnit(bufferBytesReceived, cached_rxb_diff, sizeof(bufferBytesReceived)); Meter_humanUnit(bufferBytesTransmitted, cached_txb_diff, sizeof(bufferBytesTransmitted)); - xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "rx:%siB/s tx:%siB/s", bufferBytesReceived, bufferBytesTransmitted); + xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "rx:%siB/s tx:%siB/s %d/%dpkts/s", + bufferBytesReceived, bufferBytesTransmitted, cached_rxp_diff, cached_txp_diff); } static void NetworkIOMeter_display(ATTR_UNUSED const Object* cast, RichString* out) { From 2ca75625ee5c2ac0ef1571e6918d7c94f3aa011c Mon Sep 17 00:00:00 2001 From: Christian Hesse Date: Fri, 20 May 2022 22:27:52 +0200 Subject: [PATCH 013/106] default color preset: use bold blue for better visibility The default color preset works fine on dark background, with one exception: blue characters are hardly readable. So let's make (most of) these bold. Even bold blue is fine on light background, so this sould not make anything worse. Closes #1023 (partial merge) --- CRT.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/CRT.c b/CRT.c index 22281f1e4..632548a4f 100644 --- a/CRT.c +++ b/CRT.c @@ -134,7 +134,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [METER_VALUE] = A_BOLD | ColorPair(Cyan, Black), [METER_VALUE_ERROR] = A_BOLD | ColorPair(Red, Black), [METER_VALUE_IOREAD] = ColorPair(Green, Black), - [METER_VALUE_IOWRITE] = ColorPair(Blue, Black), + [METER_VALUE_IOWRITE] = A_BOLD | ColorPair(Blue, Black), [METER_VALUE_NOTICE] = A_BOLD | ColorPair(White, Black), [METER_VALUE_OK] = ColorPair(Green, Black), [METER_VALUE_WARN] = A_BOLD | ColorPair(Yellow, Black), @@ -156,7 +156,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [PROCESS_THREAD] = ColorPair(Green, Black), [PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Green, Black), [PROCESS_COMM] = ColorPair(Magenta, Black), - [PROCESS_THREAD_COMM] = ColorPair(Blue, Black), + [PROCESS_THREAD_COMM] = A_BOLD | ColorPair(Blue, Black), [BAR_BORDER] = A_BOLD, [BAR_SHADOW] = A_BOLD | ColorPairGrayBlack, [SWAP] = ColorPair(Red, Black), @@ -164,14 +164,14 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [GRAPH_1] = A_BOLD | ColorPair(Cyan, Black), [GRAPH_2] = ColorPair(Cyan, Black), [MEMORY_USED] = ColorPair(Green, Black), - [MEMORY_BUFFERS] = ColorPair(Blue, Black), + [MEMORY_BUFFERS] = A_BOLD | ColorPair(Blue, Black), [MEMORY_BUFFERS_TEXT] = A_BOLD | ColorPair(Blue, Black), [MEMORY_CACHE] = ColorPair(Yellow, Black), [MEMORY_SHARED] = ColorPair(Magenta, Black), [HUGEPAGE_1] = ColorPair(Green, Black), [HUGEPAGE_2] = ColorPair(Yellow, Black), [HUGEPAGE_3] = ColorPair(Red, Black), - [HUGEPAGE_4] = ColorPair(Blue, Black), + [HUGEPAGE_4] = A_BOLD | ColorPair(Blue, Black), [LOAD_AVERAGE_FIFTEEN] = ColorPair(Cyan, Black), [LOAD_AVERAGE_FIVE] = A_BOLD | ColorPair(Cyan, Black), [LOAD_AVERAGE_ONE] = A_BOLD | ColorPair(White, Black), @@ -185,7 +185,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [CHECK_MARK] = A_BOLD, [CHECK_TEXT] = A_NORMAL, [HOSTNAME] = A_BOLD, - [CPU_NICE] = ColorPair(Blue, Black), + [CPU_NICE] = A_BOLD | ColorPair(Blue, Black), [CPU_NICE_TEXT] = A_BOLD | ColorPair(Blue, Black), [CPU_NORMAL] = ColorPair(Green, Black), [CPU_SYSTEM] = ColorPair(Red, Black), @@ -202,19 +202,19 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [PRESSURE_STALL_THREEHUNDRED] = ColorPair(Cyan, Black), [PRESSURE_STALL_SIXTY] = A_BOLD | ColorPair(Cyan, Black), [PRESSURE_STALL_TEN] = A_BOLD | ColorPair(White, Black), - [ZFS_MFU] = ColorPair(Blue, Black), + [ZFS_MFU] = A_BOLD | ColorPair(Blue, Black), [ZFS_MRU] = ColorPair(Yellow, Black), [ZFS_ANON] = ColorPair(Magenta, Black), [ZFS_HEADER] = ColorPair(Cyan, Black), [ZFS_OTHER] = ColorPair(Magenta, Black), - [ZFS_COMPRESSED] = ColorPair(Blue, Black), + [ZFS_COMPRESSED] = A_BOLD | ColorPair(Blue, Black), [ZFS_RATIO] = ColorPair(Magenta, Black), [ZRAM] = ColorPair(Yellow, Black), [DYNAMIC_GRAY] = ColorPairGrayBlack, [DYNAMIC_DARKGRAY] = A_BOLD | ColorPairGrayBlack, [DYNAMIC_RED] = ColorPair(Red, Black), [DYNAMIC_GREEN] = ColorPair(Green, Black), - [DYNAMIC_BLUE] = ColorPair(Blue, Black), + [DYNAMIC_BLUE] = A_BOLD | ColorPair(Blue, Black), [DYNAMIC_CYAN] = ColorPair(Cyan, Black), [DYNAMIC_MAGENTA] = ColorPair(Magenta, Black), [DYNAMIC_YELLOW] = ColorPair(Yellow, Black), From b4a4932f1fc53d75d88d8f54906acadfb3651a97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= Date: Tue, 13 Dec 2022 00:34:57 +0100 Subject: [PATCH 014/106] ScreenPanel: handle quitting panel while renaming During renaming the ScreenPanel takes ownership of the text value of the screen entries ListItem (via its saved member) and replaces the pointer in the ListItem with a pointer to the ScreenPanel's static text buffer. Restore the ownership, similar to how it's done on ESC. Fixes: #1147 --- ScreensPanel.c | 16 +++++++++++----- ScreensPanel.h | 2 +- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/ScreensPanel.c b/ScreensPanel.c index 1fd4ae568..cb664ac45 100644 --- a/ScreensPanel.c +++ b/ScreensPanel.c @@ -55,6 +55,10 @@ static void ScreensPanel_delete(Object* object) { item->ss = NULL; } + /* during renaming the ListItem's value points to our static buffer */ + if (this->renamingItem) + this->renamingItem->value = this->saved; + Panel_done(super); free(this); } @@ -89,9 +93,10 @@ static HandlerResult ScreensPanel_eventHandlerRenaming(Panel* super, int ch) { ListItem* item = (ListItem*) Panel_getSelected(super); if (!item) break; + assert(item == this->renamingItem); free(this->saved); item->value = xStrdup(this->buffer); - this->renaming = false; + this->renamingItem = NULL; super->cursorOn = false; Panel_setSelectionColor(super, PANEL_SELECTION_FOCUS); ScreensPanel_update(super); @@ -102,8 +107,9 @@ static HandlerResult ScreensPanel_eventHandlerRenaming(Panel* super, int ch) { ListItem* item = (ListItem*) Panel_getSelected(super); if (!item) break; + assert(item == this->renamingItem); item->value = this->saved; - this->renaming = false; + this->renamingItem = NULL; super->cursorOn = false; Panel_setSelectionColor(super, PANEL_SELECTION_FOCUS); break; @@ -119,7 +125,7 @@ static void startRenaming(Panel* super) { ListItem* item = (ListItem*) Panel_getSelected(super); if (item == NULL) return; - this->renaming = true; + this->renamingItem = item; super->cursorOn = true; char* name = item->value; this->saved = name; @@ -279,7 +285,7 @@ static HandlerResult ScreensPanel_eventHandlerNormal(Panel* super, int ch) { static HandlerResult ScreensPanel_eventHandler(Panel* super, int ch) { ScreensPanel* const this = (ScreensPanel*) super; - if (this->renaming) { + if (this->renamingItem) { return ScreensPanel_eventHandlerRenaming(super, ch); } else { return ScreensPanel_eventHandlerNormal(super, ch); @@ -304,7 +310,7 @@ ScreensPanel* ScreensPanel_new(Settings* settings) { this->settings = settings; this->columns = ColumnsPanel_new(settings->screens[0], columns, &(settings->changed)); this->moving = false; - this->renaming = false; + this->renamingItem = NULL; super->cursorOn = false; this->cursor = 0; Panel_setHeader(super, "Screens"); diff --git a/ScreensPanel.h b/ScreensPanel.h index 1f82395a7..88d85b5c0 100644 --- a/ScreensPanel.h +++ b/ScreensPanel.h @@ -31,7 +31,7 @@ typedef struct ScreensPanel_ { char* saved; int cursor; bool moving; - bool renaming; + ListItem *renamingItem; } ScreensPanel; typedef struct ScreenListItem_ { From b96def99168ec3be8459c2ed7148e59c326d778d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= Date: Tue, 13 Dec 2022 00:42:15 +0100 Subject: [PATCH 015/106] Unwind: use arch generic assignment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The unw_proc_info_t member unwind_info might be of different type depending on the architecture. ../../CRT.c: In function ‘print_backtrace’: ../../CRT.c:1099:23: error: assignment to ‘unw_word_t’ {aka ‘long unsigned int’} from ‘void *’ makes integer from pointer without a cast [-Werror=int-conversion] 1099 | pip.unwind_info = NULL; | ^ --- CRT.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CRT.c b/CRT.c index 632548a4f..64c259fcf 100644 --- a/CRT.c +++ b/CRT.c @@ -1096,7 +1096,7 @@ static void print_backtrace(void) { unw_get_proc_name(&cursor, symbolName, sizeof(symbolName), &offset); unw_proc_info_t pip; - pip.unwind_info = NULL; + pip.unwind_info = 0; const char* fname = "?"; const void* ptr = 0; From e9f3d96c9ae2c72a4cbd85c4ffb316ed2e0c3924 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= Date: Mon, 26 Dec 2022 17:53:46 +0100 Subject: [PATCH 016/106] Ignore clangd cache [ci skip] --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 82624490b..50f3bd15f 100644 --- a/.gitignore +++ b/.gitignore @@ -51,3 +51,6 @@ callgrind.out.* # IDE workspace configurations /.idea/ /.vscode/ + +# Language Servers +/.cache/clangd/ From 29f0b3c5e1184421ee7796ab2e1841733c45f403 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= Date: Mon, 26 Dec 2022 18:00:28 +0100 Subject: [PATCH 017/106] Use Unicode replacement character MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In unicode mode replace non-printable characters with the Unciode replacement character U+FFFD ("�"). --- RichString.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/RichString.c b/RichString.c index 3dac08321..daa0c91f9 100644 --- a/RichString.c +++ b/RichString.c @@ -61,7 +61,7 @@ static inline int RichString_writeFromWide(RichString* this, int attrs, const ch int newLen = from + len; RichString_setLen(this, newLen); for (int i = from, j = 0; i < newLen; i++, j++) { - this->chptr[i] = (CharType) { .attr = attrs & 0xffffff, .chars = { (iswprint(data[j]) ? data[j] : '?') } }; + this->chptr[i] = (CharType) { .attr = attrs & 0xffffff, .chars = { (iswprint(data[j]) ? data[j] : L'\xFFFD') } }; } return len; @@ -79,7 +79,7 @@ int RichString_appendnWideColumns(RichString* this, int attrs, const char* data_ int columnsWritten = 0; int pos = from; for (int j = 0; j < len; j++) { - wchar_t c = iswprint(data[j]) ? data[j] : '?'; + wchar_t c = iswprint(data[j]) ? data[j] : L'\xFFFD'; int cwidth = wcwidth(c); if (cwidth > *columns) break; @@ -101,7 +101,7 @@ static inline int RichString_writeFromAscii(RichString* this, int attrs, const c int newLen = from + len; RichString_setLen(this, newLen); for (int i = from, j = 0; i < newLen; i++, j++) { - this->chptr[i] = (CharType) { .attr = attrs & 0xffffff, .chars = { (isprint(data[j]) ? data[j] : '?') } }; + this->chptr[i] = (CharType) { .attr = attrs & 0xffffff, .chars = { (isprint(data[j]) ? data[j] : L'\xFFFD') } }; } return len; From 560a04ad25c77a8c5bf4cec112a3d8da8df6272c Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 7 Jan 2023 17:46:15 +0100 Subject: [PATCH 018/106] Improve code readability by creating constants for memory values --- MemoryMeter.c | 20 ++++++++++---------- MemoryMeter.h | 8 ++++++++ darwin/Platform.c | 10 +++++----- dragonflybsd/Platform.c | 10 +++++----- freebsd/Platform.c | 16 ++++++++-------- linux/Platform.c | 16 ++++++++-------- netbsd/Platform.c | 10 +++++----- openbsd/Platform.c | 10 +++++----- pcp/Platform.c | 16 ++++++++-------- solaris/Platform.c | 10 +++++----- 10 files changed, 67 insertions(+), 59 deletions(-) diff --git a/MemoryMeter.c b/MemoryMeter.c index 89e242cae..ac01dfe7d 100644 --- a/MemoryMeter.c +++ b/MemoryMeter.c @@ -29,14 +29,14 @@ static void MemoryMeter_updateValues(Meter* this) { int written; /* shared and available memory are not supported on all platforms */ - this->values[2] = NAN; - this->values[4] = NAN; + this->values[MEMORY_METER_SHARED] = NAN; + this->values[MEMORY_METER_AVAILABLE] = NAN; Platform_setMemoryValues(this); /* Do not print available memory in bar mode */ this->curItems = 4; - written = Meter_humanUnit(buffer, this->values[0], size); + written = Meter_humanUnit(buffer, this->values[MEMORY_METER_USED], size); METER_BUFFER_CHECK(buffer, size, written); METER_BUFFER_APPEND_CHR(buffer, size, '/'); @@ -52,28 +52,28 @@ static void MemoryMeter_display(const Object* cast, RichString* out) { Meter_humanUnit(buffer, this->total, sizeof(buffer)); RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer); - Meter_humanUnit(buffer, this->values[0], sizeof(buffer)); + Meter_humanUnit(buffer, this->values[MEMORY_METER_USED], sizeof(buffer)); RichString_appendAscii(out, CRT_colors[METER_TEXT], " used:"); RichString_appendAscii(out, CRT_colors[MEMORY_USED], buffer); - Meter_humanUnit(buffer, this->values[1], sizeof(buffer)); + Meter_humanUnit(buffer, this->values[MEMORY_METER_BUFFERS], sizeof(buffer)); RichString_appendAscii(out, CRT_colors[METER_TEXT], " buffers:"); RichString_appendAscii(out, CRT_colors[MEMORY_BUFFERS_TEXT], buffer); /* shared memory is not supported on all platforms */ - if (!isnan(this->values[2])) { - Meter_humanUnit(buffer, this->values[2], sizeof(buffer)); + if (!isnan(this->values[MEMORY_METER_SHARED])) { + Meter_humanUnit(buffer, this->values[MEMORY_METER_SHARED], sizeof(buffer)); RichString_appendAscii(out, CRT_colors[METER_TEXT], " shared:"); RichString_appendAscii(out, CRT_colors[MEMORY_SHARED], buffer); } - Meter_humanUnit(buffer, this->values[3], sizeof(buffer)); + Meter_humanUnit(buffer, this->values[MEMORY_METER_CACHE], sizeof(buffer)); RichString_appendAscii(out, CRT_colors[METER_TEXT], " cache:"); RichString_appendAscii(out, CRT_colors[MEMORY_CACHE], buffer); /* available memory is not supported on all platforms */ - if (!isnan(this->values[4])) { - Meter_humanUnit(buffer, this->values[4], sizeof(buffer)); + if (!isnan(this->values[MEMORY_METER_AVAILABLE])) { + Meter_humanUnit(buffer, this->values[MEMORY_METER_AVAILABLE], sizeof(buffer)); RichString_appendAscii(out, CRT_colors[METER_TEXT], " available:"); RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer); } diff --git a/MemoryMeter.h b/MemoryMeter.h index d23021e90..cc2a160c5 100644 --- a/MemoryMeter.h +++ b/MemoryMeter.h @@ -9,6 +9,14 @@ in the source distribution for its full text. #include "Meter.h" +typedef enum { + MEMORY_METER_USED = 0, + MEMORY_METER_BUFFERS = 1, + MEMORY_METER_SHARED = 2, + MEMORY_METER_CACHE = 3, + MEMORY_METER_AVAILABLE = 4, + MEMORY_METER_ITEMCOUNT = 5, // number of entries in this enum +} MemoryMeterValues; extern const MeterClass MemoryMeter_class; diff --git a/darwin/Platform.c b/darwin/Platform.c index 4b34d885a..963d526da 100644 --- a/darwin/Platform.c +++ b/darwin/Platform.c @@ -264,11 +264,11 @@ void Platform_setMemoryValues(Meter* mtr) { double page_K = (double)vm_page_size / (double)1024; mtr->total = dpl->host_info.max_mem / 1024; - mtr->values[0] = (double)(vm->active_count + vm->wire_count) * page_K; - mtr->values[1] = (double)vm->purgeable_count * page_K; - // mtr->values[2] = "shared memory, like tmpfs and shm" - mtr->values[3] = (double)vm->inactive_count * page_K; - // mtr->values[4] = "available memory" + mtr->values[MEMORY_METER_USED] = (double)(vm->active_count + vm->wire_count) * page_K; + mtr->values[MEMORY_METER_BUFFERS] = (double)vm->purgeable_count * page_K; + // mtr->values[MEMORY_METER_SHARED] = "shared memory, like tmpfs and shm" + mtr->values[MEMORY_METER_CACHE] = (double)vm->inactive_count * page_K; + // mtr->values[MEMORY_METER_AVAILABLE] = "available memory" } void Platform_setSwapValues(Meter* mtr) { diff --git a/dragonflybsd/Platform.c b/dragonflybsd/Platform.c index ea5f4fa47..b3a82bf3a 100644 --- a/dragonflybsd/Platform.c +++ b/dragonflybsd/Platform.c @@ -208,11 +208,11 @@ void Platform_setMemoryValues(Meter* this) { const ProcessList* pl = this->pl; this->total = pl->totalMem; - this->values[0] = pl->usedMem; - this->values[1] = pl->buffersMem; - // this->values[2] = "shared memory, like tmpfs and shm" - this->values[3] = pl->cachedMem; - // this->values[4] = "available memory" + this->values[MEMORY_METER_USED] = pl->usedMem; + this->values[MEMORY_METER_BUFFERS] = pl->buffersMem; + // this->values[MEMORY_METER_SHARED] = "shared memory, like tmpfs and shm" + this->values[MEMORY_METER_CACHE] = pl->cachedMem; + // this->values[MEMORY_METER_AVAILABLE] = "available memory" } void Platform_setSwapValues(Meter* this) { diff --git a/freebsd/Platform.c b/freebsd/Platform.c index 1a731b714..3e8faaeaf 100644 --- a/freebsd/Platform.c +++ b/freebsd/Platform.c @@ -230,20 +230,20 @@ void Platform_setMemoryValues(Meter* this) { const FreeBSDProcessList* fpl = (const FreeBSDProcessList*) pl; this->total = pl->totalMem; - this->values[0] = pl->usedMem; - this->values[1] = pl->buffersMem; - // this->values[2] = "shared memory, like tmpfs and shm" - this->values[3] = pl->cachedMem; - // this->values[4] = "available memory" + this->values[MEMORY_METER_USED] = pl->usedMem; + this->values[MEMORY_METER_BUFFERS] = pl->buffersMem; + // this->values[MEMORY_METER_SHARED] = "shared memory, like tmpfs and shm" + this->values[MEMORY_METER_CACHE] = pl->cachedMem; + // this->values[MEMORY_METER_AVAILABLE] = "available memory" if (fpl->zfs.enabled) { // ZFS does not shrink below the value of zfs_arc_min. unsigned long long int shrinkableSize = 0; if (fpl->zfs.size > fpl->zfs.min) shrinkableSize = fpl->zfs.size - fpl->zfs.min; - this->values[0] -= shrinkableSize; - this->values[3] += shrinkableSize; - // this->values[4] += shrinkableSize; + this->values[MEMORY_METER_USED] -= shrinkableSize; + this->values[MEMORY_METER_CACHE] += shrinkableSize; + // this->values[MEMORY_METER_AVAILABLE] += shrinkableSize; } } diff --git a/linux/Platform.c b/linux/Platform.c index f9cd4d503..8c608d6d2 100644 --- a/linux/Platform.c +++ b/linux/Platform.c @@ -351,20 +351,20 @@ void Platform_setMemoryValues(Meter* this) { const LinuxProcessList* lpl = (const LinuxProcessList*) pl; this->total = pl->totalMem; - this->values[0] = pl->usedMem; - this->values[1] = pl->buffersMem; - this->values[2] = pl->sharedMem; - this->values[3] = pl->cachedMem; - this->values[4] = pl->availableMem; + this->values[MEMORY_METER_USED] = pl->usedMem; + this->values[MEMORY_METER_BUFFERS] = pl->buffersMem; + this->values[MEMORY_METER_SHARED] = pl->sharedMem; + this->values[MEMORY_METER_CACHE] = pl->cachedMem; + this->values[MEMORY_METER_AVAILABLE] = pl->availableMem; if (lpl->zfs.enabled != 0 && !Running_containerized) { // ZFS does not shrink below the value of zfs_arc_min. unsigned long long int shrinkableSize = 0; if (lpl->zfs.size > lpl->zfs.min) shrinkableSize = lpl->zfs.size - lpl->zfs.min; - this->values[0] -= shrinkableSize; - this->values[3] += shrinkableSize; - this->values[4] += shrinkableSize; + this->values[MEMORY_METER_USED] -= shrinkableSize; + this->values[MEMORY_METER_CACHE] += shrinkableSize; + this->values[MEMORY_METER_AVAILABLE] += shrinkableSize; } } diff --git a/netbsd/Platform.c b/netbsd/Platform.c index 5b4fd3958..326458f0f 100644 --- a/netbsd/Platform.c +++ b/netbsd/Platform.c @@ -270,11 +270,11 @@ double Platform_setCPUValues(Meter* this, int cpu) { void Platform_setMemoryValues(Meter* this) { const ProcessList* pl = this->pl; this->total = pl->totalMem; - this->values[0] = pl->usedMem; - this->values[1] = pl->buffersMem; - // this->values[2] = "shared memory, like tmpfs and shm" - this->values[3] = pl->cachedMem; - // this->values[4] = "available memory" + this->values[MEMORY_METER_USED] = pl->usedMem; + this->values[MEMORY_METER_BUFFERS] = pl->buffersMem; + // this->values[MEMORY_METER_SHARED] = "shared memory, like tmpfs and shm" + this->values[MEMORY_METER_CACHE] = pl->cachedMem; + // this->values[MEMORY_METER_AVAILABLE] = "available memory" } void Platform_setSwapValues(Meter* this) { diff --git a/openbsd/Platform.c b/openbsd/Platform.c index b222bee0c..6e9d6cdfe 100644 --- a/openbsd/Platform.c +++ b/openbsd/Platform.c @@ -227,11 +227,11 @@ void Platform_setMemoryValues(Meter* this) { long int cachedMem = pl->cachedMem; usedMem -= buffersMem + cachedMem; this->total = pl->totalMem; - this->values[0] = usedMem; - this->values[1] = buffersMem; - // this->values[2] = "shared memory, like tmpfs and shm" - this->values[3] = cachedMem; - // this->values[4] = "available memory" + this->values[MEMORY_METER_USED] = usedMem; + this->values[MEMORY_METER_BUFFERS] = buffersMem; + // this->values[MEMORY_METER_SHARED] = "shared memory, like tmpfs and shm" + this->values[MEMORY_METER_CACHE] = cachedMem; + // this->values[MEMORY_METER_AVAILABLE] = "available memory" } void Platform_setSwapValues(Meter* this) { diff --git a/pcp/Platform.c b/pcp/Platform.c index 2987c8e94..521e5f11d 100644 --- a/pcp/Platform.c +++ b/pcp/Platform.c @@ -528,20 +528,20 @@ void Platform_setMemoryValues(Meter* this) { const PCPProcessList* ppl = (const PCPProcessList*) pl; this->total = pl->totalMem; - this->values[0] = pl->usedMem; - this->values[1] = pl->buffersMem; - this->values[2] = pl->sharedMem; - this->values[3] = pl->cachedMem; - this->values[4] = pl->availableMem; + this->values[MEMORY_METER_USED] = pl->usedMem; + this->values[MEMORY_METER_BUFFERS] = pl->buffersMem; + this->values[MEMORY_METER_SHARED] = pl->sharedMem; + this->values[MEMORY_METER_CACHE] = pl->cachedMem; + this->values[MEMORY_METER_AVAILABLE] = pl->availableMem; if (ppl->zfs.enabled != 0) { // ZFS does not shrink below the value of zfs_arc_min. unsigned long long int shrinkableSize = 0; if (ppl->zfs.size > ppl->zfs.min) shrinkableSize = ppl->zfs.size - ppl->zfs.min; - this->values[0] -= shrinkableSize; - this->values[3] += shrinkableSize; - this->values[4] += shrinkableSize; + this->values[MEMORY_METER_USED] -= shrinkableSize; + this->values[MEMORY_METER_CACHE] += shrinkableSize; + this->values[MEMORY_METER_AVAILABLE] += shrinkableSize; } } diff --git a/solaris/Platform.c b/solaris/Platform.c index 9c5acb5b3..4d1b2487f 100644 --- a/solaris/Platform.c +++ b/solaris/Platform.c @@ -237,11 +237,11 @@ double Platform_setCPUValues(Meter* this, unsigned int cpu) { void Platform_setMemoryValues(Meter* this) { const ProcessList* pl = this->pl; this->total = pl->totalMem; - this->values[0] = pl->usedMem; - this->values[1] = pl->buffersMem; - // this->values[2] = "shared memory, like tmpfs and shm" - this->values[3] = pl->cachedMem; - // this->values[4] = "available memory" + this->values[MEMORY_METER_USED] = pl->usedMem; + this->values[MEMORY_METER_BUFFERS] = pl->buffersMem; + // this->values[MEMORY_METER_SHARED] = "shared memory, like tmpfs and shm" + this->values[MEMORY_METER_CACHE] = pl->cachedMem; + // this->values[MEMORY_METER_AVAILABLE] = "available memory" } void Platform_setSwapValues(Meter* this) { From f320171578ba37d085e5daabfea8b809c3c62633 Mon Sep 17 00:00:00 2001 From: Daniel Lange Date: Sun, 8 Jan 2023 16:02:34 +0100 Subject: [PATCH 019/106] Updates for 2023. Happy New Year! --- configure.ac | 2 +- htop.1.in | 4 ++-- pcp-htop.5.in | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/configure.ac b/configure.ac index 531633665..37dba53a4 100644 --- a/configure.ac +++ b/configure.ac @@ -727,7 +727,7 @@ AC_SUBST([AM_CPPFLAGS]) # We're done, let's go! # ---------------------------------------------------------------------- -AC_DEFINE_UNQUOTED([COPYRIGHT], ["(C) 2004-2019 Hisham Muhammad. (C) 2020-2022 htop dev team."], [Copyright message.]) +AC_DEFINE_UNQUOTED([COPYRIGHT], ["(C) 2004-2019 Hisham Muhammad. (C) 2020-2023 htop dev team."], [Copyright message.]) AM_CONDITIONAL([HTOP_LINUX], [test "$my_htop_platform" = linux]) AM_CONDITIONAL([HTOP_FREEBSD], [test "$my_htop_platform" = freebsd]) diff --git a/htop.1.in b/htop.1.in index 1bc76a3c7..eefc37f52 100644 --- a/htop.1.in +++ b/htop.1.in @@ -1,4 +1,4 @@ -.TH "HTOP" "1" "2022" "@PACKAGE_STRING@" "User Commands" +.TH "HTOP" "1" "2023" "@PACKAGE_STRING@" "User Commands" .SH "NAME" htop, pcp-htop \- interactive process viewer .SH "SYNOPSIS" @@ -669,7 +669,7 @@ communities, and forms part of the Performance Co-Pilot suite of tools. .SH "COPYRIGHT" Copyright \(co 2004-2019 Hisham Muhammad. .br -Copyright \(co 2020-2022 htop dev team. +Copyright \(co 2020-2023 htop dev team. .LP License GPLv2+: GNU General Public License version 2 or, at your option, any later version. .LP diff --git a/pcp-htop.5.in b/pcp-htop.5.in index 0f5cb14f0..dbc0dfb25 100644 --- a/pcp-htop.5.in +++ b/pcp-htop.5.in @@ -1,4 +1,4 @@ -.TH "PCP-HTOP" "5" "2022" "@PACKAGE_STRING@" "File Formats" +.TH "PCP-HTOP" "5" "2023" "@PACKAGE_STRING@" "File Formats" .SH "NAME" \f3pcp-htop\f1 \- pcp-htop configuration file .SH "DESCRIPTION" From aa48120a160b1eadf7d87e46385767cec5392c8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=BD=D0=B0=D0=B1?= Date: Sat, 26 Nov 2022 00:34:59 +0100 Subject: [PATCH 020/106] Fix Linux Platform_getProcessLocks() for file-description locks Instead of filtering the global /proc/locks by PID-that-locked (which is different than the process that holds it for inherited flock(2)s and always literal -1 for fcntl(F_OFD_SETLK)s), just look at the locks on open files For a process with: $ head /proc/$$/fdinfo/{9,10,11} ==> /proc/797393/fdinfo/9 <== pos: 0 flags: 0100000 mnt_id: 2775 lock: 1: FLOCK ADVISORY WRITE 818754 00:69:7 0 EOF ==> /proc/797393/fdinfo/10 <== pos: 0 flags: 0100000 mnt_id: 127 lock: 1: FLOCK ADVISORY WRITE 828244 00:30:509028 0 EOF lock: 2: OFDLCK ADVISORY READ -1 00:30:509028 0 199 ==> /proc/797393/fdinfo/11 <== pos: 0 flags: 0100000 mnt_id: 2775 lock: 1: FLOCK ADVISORY WRITE 935925 00:69:1187 0 EOF Instead of: No locks have been found for the selected process. I now observe: 1 FLOCK ADVISORY WRITE 00:30:00000000000000509028 0 /home/nabijaczleweli/.feeds 1 FLOCK ADVISORY WRITE 00:69:00000000000000000007 0 /home/nabijaczleweli/uwu/mart.html 1 FLOCK ADVISORY WRITE 00:69:00000000000000001187 0 /home/nabijaczleweli/uwu/removedfile (deleted) 2 OFDLCK ADVISORY READ 00:30:00000000000000509028 0 199 /home/nabijaczleweli/.feeds This also fixes an obvious issue where the filename for fd 0 would never be resolved --- linux/Platform.c | 107 +++++++++++++++++++++++++++++------------------ 1 file changed, 67 insertions(+), 40 deletions(-) diff --git a/linux/Platform.c b/linux/Platform.c index 8c608d6d2..38f9854d4 100644 --- a/linux/Platform.c +++ b/linux/Platform.c @@ -12,6 +12,7 @@ in the source distribution for its full text. #include #include #include +#include #include #include #include @@ -487,60 +488,86 @@ char* Platform_getInodeFilename(pid_t pid, ino_t inode) { FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) { FileLocks_ProcessData* pdata = xCalloc(1, sizeof(FileLocks_ProcessData)); + DIR* dirp; + int dfd; + + char path[PATH_MAX]; + xSnprintf(path, sizeof(path), PROCDIR "/%d/fdinfo/", pid); + if (strlen(path) >= (sizeof(path) - 2)) + goto err; + + if (!(dirp = opendir(path))) + goto err; - FILE* f = fopen(PROCDIR "/locks", "r"); - if (!f) { - pdata->error = true; - return pdata; + if ((dfd = dirfd(dirp)) == -1) { + closedir(dirp); + goto err; } - char buffer[1024]; FileLocks_LockData** data_ref = &pdata->locks; - while (fgets(buffer, sizeof(buffer), f)) { - if (!strchr(buffer, '\n')) + for (struct dirent* de; (de = readdir(dirp)); ) { + if (String_eq(de->d_name, ".") || String_eq(de->d_name, "..")) continue; - int lock_id; - char lock_type[16]; - char lock_excl[16]; - char lock_rw[16]; - pid_t lock_pid; - unsigned int lock_dev[2]; - uint64_t lock_inode; - char lock_start[25]; - char lock_end[25]; - - if (10 != sscanf(buffer, "%d: %15s %15s %15s %d %x:%x:%"PRIu64" %24s %24s", - &lock_id, lock_type, lock_excl, lock_rw, &lock_pid, - &lock_dev[0], &lock_dev[1], &lock_inode, - lock_start, lock_end)) + errno = 0; + char *end = de->d_name; + strtoull(de->d_name, &end, 10); + if (errno || *end) continue; - if (pid != lock_pid) + int fd = openat(dfd, de->d_name, O_RDONLY | O_CLOEXEC); + if(fd == -1) continue; + FILE *f = fdopen(fd, "r"); + if(!f) { + close(fd); + continue; + } - FileLocks_LockData* ldata = xCalloc(1, sizeof(FileLocks_LockData)); - FileLocks_Data* data = &ldata->data; - data->id = lock_id; - data->locktype = xStrdup(lock_type); - data->exclusive = xStrdup(lock_excl); - data->readwrite = xStrdup(lock_rw); - data->filename = Platform_getInodeFilename(lock_pid, lock_inode); - data->dev[0] = lock_dev[0]; - data->dev[1] = lock_dev[1]; - data->inode = lock_inode; - data->start = strtoull(lock_start, NULL, 10); - if (!String_eq(lock_end, "EOF")) { - data->end = strtoull(lock_end, NULL, 10); - } else { - data->end = ULLONG_MAX; + for (char buffer[1024]; fgets(buffer, sizeof(buffer), f); ) { + if (!strchr(buffer, '\n')) + continue; + + if (strncmp(buffer, "lock:\t", strlen("lock:\t"))) + continue; + + FileLocks_Data data = {0}; + int _; + char lock_end[25], locktype[32], exclusive[32], readwrite[32]; + if (10 != sscanf(buffer + strlen("lock:\t"), "%d: %31s %31s %31s %d %x:%x:%"PRIu64" %"PRIu64" %24s", + &data.id, locktype, exclusive, readwrite, &_, + &data.dev[0], &data.dev[1], &data.inode, + &data.start, lock_end)) + continue; + + data.locktype = xStrdup(locktype); + data.exclusive = xStrdup(exclusive); + data.readwrite = xStrdup(readwrite); + + if (String_eq(lock_end, "EOF")) + data.end = ULLONG_MAX; + else + data.end = strtoull(lock_end, NULL, 10); + + xSnprintf(path, sizeof(path), PROCDIR "/%d/fd/%s", pid, de->d_name); + char link[PATH_MAX]; + ssize_t link_len; + if (strlen(path) < (sizeof(path) - 2) && (link_len = readlink(path, link, sizeof(link))) != -1) + data.filename = xStrndup(link, link_len); + + *data_ref = xCalloc(1, sizeof(FileLocks_LockData)); + (*data_ref)->data = data; + data_ref = &(*data_ref)->next; } - *data_ref = ldata; - data_ref = &ldata->next; + fclose(f); } - fclose(f); + closedir(dirp); + return pdata; + +err: + pdata->error = true; return pdata; } From 67bc7fe640bed8b8fa8006c9152442023928b756 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=BD=D0=B0=D0=B1?= Date: Sat, 26 Nov 2022 00:48:54 +0100 Subject: [PATCH 021/106] Clean out Platform_getInodeFilename It's an artefact of the previous implementation of Platform_getProcessLocks for Linux, and is never used; there's no reason for it to have ever been exported --- darwin/Platform.c | 6 ----- darwin/Platform.h | 2 -- dragonflybsd/Platform.c | 6 ----- dragonflybsd/Platform.h | 2 -- freebsd/Platform.c | 6 ----- freebsd/Platform.h | 2 -- linux/Platform.c | 55 ----------------------------------------- linux/Platform.h | 2 -- netbsd/Platform.c | 6 ----- netbsd/Platform.h | 2 -- openbsd/Platform.c | 6 ----- openbsd/Platform.h | 2 -- pcp/Platform.c | 6 ----- pcp/Platform.h | 2 -- solaris/Platform.c | 6 ----- solaris/Platform.h | 2 -- unsupported/Platform.c | 6 ----- unsupported/Platform.h | 2 -- 18 files changed, 121 deletions(-) diff --git a/darwin/Platform.c b/darwin/Platform.c index 963d526da..e9d6caab1 100644 --- a/darwin/Platform.c +++ b/darwin/Platform.c @@ -344,12 +344,6 @@ char* Platform_getProcessEnv(pid_t pid) { return env; } -char* Platform_getInodeFilename(pid_t pid, ino_t inode) { - (void)pid; - (void)inode; - return NULL; -} - FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) { (void)pid; return NULL; diff --git a/darwin/Platform.h b/darwin/Platform.h index 3fd46add2..66362072e 100644 --- a/darwin/Platform.h +++ b/darwin/Platform.h @@ -68,8 +68,6 @@ void Platform_setZfsCompressedArcValues(Meter* this); char* Platform_getProcessEnv(pid_t pid); -char* Platform_getInodeFilename(pid_t pid, ino_t inode); - FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid); bool Platform_getDiskIO(DiskIOData* data); diff --git a/dragonflybsd/Platform.c b/dragonflybsd/Platform.c index b3a82bf3a..944df990c 100644 --- a/dragonflybsd/Platform.c +++ b/dragonflybsd/Platform.c @@ -228,12 +228,6 @@ char* Platform_getProcessEnv(pid_t pid) { return NULL; } -char* Platform_getInodeFilename(pid_t pid, ino_t inode) { - (void)pid; - (void)inode; - return NULL; -} - FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) { (void)pid; return NULL; diff --git a/dragonflybsd/Platform.h b/dragonflybsd/Platform.h index 3de877188..cf6935628 100644 --- a/dragonflybsd/Platform.h +++ b/dragonflybsd/Platform.h @@ -59,8 +59,6 @@ void Platform_setSwapValues(Meter* this); char* Platform_getProcessEnv(pid_t pid); -char* Platform_getInodeFilename(pid_t pid, ino_t inode); - FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid); bool Platform_getDiskIO(DiskIOData* data); diff --git a/freebsd/Platform.c b/freebsd/Platform.c index 3e8faaeaf..3f69d2072 100644 --- a/freebsd/Platform.c +++ b/freebsd/Platform.c @@ -287,12 +287,6 @@ char* Platform_getProcessEnv(pid_t pid) { return env; } -char* Platform_getInodeFilename(pid_t pid, ino_t inode) { - (void)pid; - (void)inode; - return NULL; -} - FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) { (void)pid; return NULL; diff --git a/freebsd/Platform.h b/freebsd/Platform.h index 555ab902a..51269c091 100644 --- a/freebsd/Platform.h +++ b/freebsd/Platform.h @@ -59,8 +59,6 @@ void Platform_setZfsCompressedArcValues(Meter* this); char* Platform_getProcessEnv(pid_t pid); -char* Platform_getInodeFilename(pid_t pid, ino_t inode); - FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid); bool Platform_getDiskIO(DiskIOData* data); diff --git a/linux/Platform.c b/linux/Platform.c index 38f9854d4..d4972d41e 100644 --- a/linux/Platform.c +++ b/linux/Platform.c @@ -431,61 +431,6 @@ char* Platform_getProcessEnv(pid_t pid) { return env; } -/* - * Return the absolute path of a file given its pid&inode number - * - * Based on implementation of lslocks from util-linux: - * https://sources.debian.org/src/util-linux/2.36-3/misc-utils/lslocks.c/#L162 - */ -char* Platform_getInodeFilename(pid_t pid, ino_t inode) { - struct stat sb; - const struct dirent* de; - DIR* dirp; - ssize_t len; - int fd; - - char path[PATH_MAX]; - char sym[PATH_MAX]; - char* ret = NULL; - - memset(path, 0, sizeof(path)); - memset(sym, 0, sizeof(sym)); - - xSnprintf(path, sizeof(path), "%s/%d/fd/", PROCDIR, pid); - if (strlen(path) >= (sizeof(path) - 2)) - return NULL; - - if (!(dirp = opendir(path))) - return NULL; - - if ((fd = dirfd(dirp)) < 0 ) - goto out; - - while ((de = readdir(dirp))) { - if (String_eq(de->d_name, ".") || String_eq(de->d_name, "..")) - continue; - - /* care only for numerical descriptors */ - if (!strtoull(de->d_name, (char **) NULL, 10)) - continue; - - if (!Compat_fstatat(fd, path, de->d_name, &sb, 0) && inode != sb.st_ino) - continue; - - if ((len = Compat_readlinkat(fd, path, de->d_name, sym, sizeof(sym) - 1)) < 1) - goto out; - - sym[len] = '\0'; - - ret = xStrdup(sym); - break; - } - -out: - closedir(dirp); - return ret; -} - FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) { FileLocks_ProcessData* pdata = xCalloc(1, sizeof(FileLocks_ProcessData)); DIR* dirp; diff --git a/linux/Platform.h b/linux/Platform.h index c8849684e..f6ac18804 100644 --- a/linux/Platform.h +++ b/linux/Platform.h @@ -75,8 +75,6 @@ void Platform_setZfsCompressedArcValues(Meter* this); char* Platform_getProcessEnv(pid_t pid); -char* Platform_getInodeFilename(pid_t pid, ino_t inode); - FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid); void Platform_getPressureStall(const char* file, bool some, double* ten, double* sixty, double* threehundred); diff --git a/netbsd/Platform.c b/netbsd/Platform.c index 326458f0f..e2f8c8800 100644 --- a/netbsd/Platform.c +++ b/netbsd/Platform.c @@ -338,12 +338,6 @@ char* Platform_getProcessEnv(pid_t pid) { return env; } -char* Platform_getInodeFilename(pid_t pid, ino_t inode) { - (void)pid; - (void)inode; - return NULL; -} - FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) { (void)pid; return NULL; diff --git a/netbsd/Platform.h b/netbsd/Platform.h index f2e5823d1..0e53b4570 100644 --- a/netbsd/Platform.h +++ b/netbsd/Platform.h @@ -65,8 +65,6 @@ void Platform_setSwapValues(Meter* this); char* Platform_getProcessEnv(pid_t pid); -char* Platform_getInodeFilename(pid_t pid, ino_t inode); - FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid); bool Platform_getDiskIO(DiskIOData* data); diff --git a/openbsd/Platform.c b/openbsd/Platform.c index 6e9d6cdfe..74247484b 100644 --- a/openbsd/Platform.c +++ b/openbsd/Platform.c @@ -296,12 +296,6 @@ char* Platform_getProcessEnv(pid_t pid) { return env; } -char* Platform_getInodeFilename(pid_t pid, ino_t inode) { - (void)pid; - (void)inode; - return NULL; -} - FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) { (void)pid; return NULL; diff --git a/openbsd/Platform.h b/openbsd/Platform.h index 19818ff9c..27d792e0d 100644 --- a/openbsd/Platform.h +++ b/openbsd/Platform.h @@ -57,8 +57,6 @@ void Platform_setSwapValues(Meter* this); char* Platform_getProcessEnv(pid_t pid); -char* Platform_getInodeFilename(pid_t pid, ino_t inode); - FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid); bool Platform_getDiskIO(DiskIOData* data); diff --git a/pcp/Platform.c b/pcp/Platform.c index 521e5f11d..d1bd0bc43 100644 --- a/pcp/Platform.c +++ b/pcp/Platform.c @@ -669,12 +669,6 @@ char* Platform_getProcessEnv(pid_t pid) { return value.cp; } -char* Platform_getInodeFilename(pid_t pid, ino_t inode) { - (void)pid; - (void)inode; - return NULL; -} - FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) { (void)pid; return NULL; diff --git a/pcp/Platform.h b/pcp/Platform.h index dc51c736c..f06f22664 100644 --- a/pcp/Platform.h +++ b/pcp/Platform.h @@ -98,8 +98,6 @@ void Platform_setZfsCompressedArcValues(Meter* this); char* Platform_getProcessEnv(pid_t pid); -char* Platform_getInodeFilename(pid_t pid, ino_t inode); - FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid); void Platform_getPressureStall(const char* file, bool some, double* ten, double* sixty, double* threehundred); diff --git a/solaris/Platform.c b/solaris/Platform.c index 4d1b2487f..a99052c34 100644 --- a/solaris/Platform.c +++ b/solaris/Platform.c @@ -308,12 +308,6 @@ char* Platform_getProcessEnv(pid_t pid) { return xRealloc(envBuilder.env, envBuilder.size + 1); } -char* Platform_getInodeFilename(pid_t pid, ino_t inode) { - (void)pid; - (void)inode; - return NULL; -} - FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) { (void)pid; return NULL; diff --git a/solaris/Platform.h b/solaris/Platform.h index 136db197a..14431e356 100644 --- a/solaris/Platform.h +++ b/solaris/Platform.h @@ -86,8 +86,6 @@ void Platform_setZfsCompressedArcValues(Meter* this); char* Platform_getProcessEnv(pid_t pid); -char* Platform_getInodeFilename(pid_t pid, ino_t inode); - FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid); bool Platform_getDiskIO(DiskIOData* data); diff --git a/unsupported/Platform.c b/unsupported/Platform.c index 33d7c4151..fcccb21a9 100644 --- a/unsupported/Platform.c +++ b/unsupported/Platform.c @@ -129,12 +129,6 @@ char* Platform_getProcessEnv(pid_t pid) { return NULL; } -char* Platform_getInodeFilename(pid_t pid, ino_t inode) { - (void)pid; - (void)inode; - return NULL; -} - FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) { (void)pid; return NULL; diff --git a/unsupported/Platform.h b/unsupported/Platform.h index 69191e350..f475dda4d 100644 --- a/unsupported/Platform.h +++ b/unsupported/Platform.h @@ -50,8 +50,6 @@ void Platform_setSwapValues(Meter* this); char* Platform_getProcessEnv(pid_t pid); -char* Platform_getInodeFilename(pid_t pid, ino_t inode); - FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid); bool Platform_getDiskIO(DiskIOData* data); From 8def4d63cd9c216d89b785f858c866257f9a6768 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=BD=D0=B0=D0=B1?= Date: Sat, 26 Nov 2022 00:58:14 +0100 Subject: [PATCH 022/106] Replace meaningless ID column with FD column in lock screen Doubly meaningless since the IDs are now per-FD --- ProcessLocksScreen.c | 10 +++++----- ProcessLocksScreen.h | 2 +- linux/Platform.c | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ProcessLocksScreen.c b/ProcessLocksScreen.c index b842d2b95..5f876e5df 100644 --- a/ProcessLocksScreen.c +++ b/ProcessLocksScreen.c @@ -27,7 +27,7 @@ ProcessLocksScreen* ProcessLocksScreen_new(const Process* process) { this->pid = process->tgid; else this->pid = process->pid; - return (ProcessLocksScreen*) InfoScreen_init(&this->super, process, NULL, LINES - 2, " ID TYPE EXCLUSION READ/WRITE DEVICE:INODE START END FILENAME"); + return (ProcessLocksScreen*) InfoScreen_init(&this->super, process, NULL, LINES - 2, " FD TYPE EXCLUSION READ/WRITE DEVICE:INODE START END FILENAME"); } void ProcessLocksScreen_delete(Object* this) { @@ -64,16 +64,16 @@ static void ProcessLocksScreen_scan(InfoScreen* this) { char entry[512]; if (ULLONG_MAX == data->end) { - xSnprintf(entry, sizeof(entry), "%10d %-10s %-10s %-10s %02x:%02x:%020"PRIu64" %20"PRIu64" %20s %s", - data->id, + xSnprintf(entry, sizeof(entry), "%5d %-10s %-10s %-10s %02x:%02x:%020"PRIu64" %20"PRIu64" %20s %s", + data->fd, data->locktype, data->exclusive, data->readwrite, data->dev[0], data->dev[1], data->inode, data->start, "", data->filename ? data->filename : "" ); } else { - xSnprintf(entry, sizeof(entry), "%10d %-10s %-10s %-10s %02x:%02x:%020"PRIu64" %20"PRIu64" %20"PRIu64" %s", - data->id, + xSnprintf(entry, sizeof(entry), "%5d %-10s %-10s %-10s %02x:%02x:%020"PRIu64" %20"PRIu64" %20"PRIu64" %s", + data->fd, data->locktype, data->exclusive, data->readwrite, data->dev[0], data->dev[1], data->inode, data->start, data->end, diff --git a/ProcessLocksScreen.h b/ProcessLocksScreen.h index cf34de4fe..560f3a88d 100644 --- a/ProcessLocksScreen.h +++ b/ProcessLocksScreen.h @@ -26,7 +26,7 @@ typedef struct FileLocks_Data_ { char* exclusive; char* readwrite; char* filename; - int id; + int fd; unsigned int dev[2]; uint64_t inode; uint64_t start; diff --git a/linux/Platform.c b/linux/Platform.c index d4972d41e..4382c1ee1 100644 --- a/linux/Platform.c +++ b/linux/Platform.c @@ -456,7 +456,7 @@ FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) { errno = 0; char *end = de->d_name; - strtoull(de->d_name, &end, 10); + int file = strtoull(de->d_name, &end, 10); if (errno || *end) continue; @@ -476,11 +476,11 @@ FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) { if (strncmp(buffer, "lock:\t", strlen("lock:\t"))) continue; - FileLocks_Data data = {0}; + FileLocks_Data data = {.fd = file}; int _; char lock_end[25], locktype[32], exclusive[32], readwrite[32]; if (10 != sscanf(buffer + strlen("lock:\t"), "%d: %31s %31s %31s %d %x:%x:%"PRIu64" %"PRIu64" %24s", - &data.id, locktype, exclusive, readwrite, &_, + &_, locktype, exclusive, readwrite, &_, &data.dev[0], &data.dev[1], &data.inode, &data.start, lock_end)) continue; From 650cf0f13bf667270d0a6a4612437af264667585 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=BD=D0=B0=D0=B1?= Date: Sat, 26 Nov 2022 01:21:57 +0100 Subject: [PATCH 023/106] Use a 0x{devnum} format for devices in lock screen, like files screen Also reflow it to not be absolutely bonkers --- ProcessLocksScreen.c | 11 ++++++----- ProcessLocksScreen.h | 2 +- linux/Platform.c | 5 ++++- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/ProcessLocksScreen.c b/ProcessLocksScreen.c index 5f876e5df..57c9ce75f 100644 --- a/ProcessLocksScreen.c +++ b/ProcessLocksScreen.c @@ -27,7 +27,8 @@ ProcessLocksScreen* ProcessLocksScreen_new(const Process* process) { this->pid = process->tgid; else this->pid = process->pid; - return (ProcessLocksScreen*) InfoScreen_init(&this->super, process, NULL, LINES - 2, " FD TYPE EXCLUSION READ/WRITE DEVICE:INODE START END FILENAME"); + + return (ProcessLocksScreen*) InfoScreen_init(&this->super, process, NULL, LINES - 2, " FD TYPE EXCLUSION READ/WRITE DEVICE NODE START END FILENAME"); } void ProcessLocksScreen_delete(Object* this) { @@ -64,18 +65,18 @@ static void ProcessLocksScreen_scan(InfoScreen* this) { char entry[512]; if (ULLONG_MAX == data->end) { - xSnprintf(entry, sizeof(entry), "%5d %-10s %-10s %-10s %02x:%02x:%020"PRIu64" %20"PRIu64" %20s %s", + xSnprintf(entry, sizeof(entry), "%5d %-10s %-10s %-10s %#6"PRIx64" %10"PRIu64" %19"PRIu64" %19s %s", data->fd, data->locktype, data->exclusive, data->readwrite, - data->dev[0], data->dev[1], data->inode, + (uint64_t) data->dev, data->inode, data->start, "", data->filename ? data->filename : "" ); } else { - xSnprintf(entry, sizeof(entry), "%5d %-10s %-10s %-10s %02x:%02x:%020"PRIu64" %20"PRIu64" %20"PRIu64" %s", + xSnprintf(entry, sizeof(entry), "%5d %-10s %-10s %-10s %#6"PRIx64" %10"PRIu64" %19"PRIu64" %19"PRIu64" %s", data->fd, data->locktype, data->exclusive, data->readwrite, - data->dev[0], data->dev[1], data->inode, + (uint64_t) data->dev, data->inode, data->start, data->end, data->filename ? data->filename : "" ); diff --git a/ProcessLocksScreen.h b/ProcessLocksScreen.h index 560f3a88d..417df7b20 100644 --- a/ProcessLocksScreen.h +++ b/ProcessLocksScreen.h @@ -27,7 +27,7 @@ typedef struct FileLocks_Data_ { char* readwrite; char* filename; int fd; - unsigned int dev[2]; + dev_t dev; uint64_t inode; uint64_t start; uint64_t end; diff --git a/linux/Platform.c b/linux/Platform.c index 4382c1ee1..64f25c420 100644 --- a/linux/Platform.c +++ b/linux/Platform.c @@ -22,6 +22,7 @@ in the source distribution for its full text. #include #include #include +#include #include "BatteryMeter.h" #include "ClockMeter.h" @@ -478,16 +479,18 @@ FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) { FileLocks_Data data = {.fd = file}; int _; + unsigned int maj, min; char lock_end[25], locktype[32], exclusive[32], readwrite[32]; if (10 != sscanf(buffer + strlen("lock:\t"), "%d: %31s %31s %31s %d %x:%x:%"PRIu64" %"PRIu64" %24s", &_, locktype, exclusive, readwrite, &_, - &data.dev[0], &data.dev[1], &data.inode, + &maj, &min, &data.inode, &data.start, lock_end)) continue; data.locktype = xStrdup(locktype); data.exclusive = xStrdup(exclusive); data.readwrite = xStrdup(readwrite); + data.dev = makedev(maj, min); if (String_eq(lock_end, "EOF")) data.end = ULLONG_MAX; From d97cf58261d340922c85bee9b9e1670b4f88eafc Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 8 Jan 2023 21:38:57 +0100 Subject: [PATCH 024/106] Improve code readability by creating constants for SWAP memory values --- SwapMeter.c | 6 +++--- SwapMeter.h | 5 +++++ darwin/Platform.c | 2 +- dragonflybsd/Platform.c | 4 ++-- freebsd/Platform.c | 4 ++-- netbsd/Platform.c | 4 ++-- openbsd/Platform.c | 4 ++-- pcp/Platform.c | 4 ++-- solaris/Platform.c | 4 ++-- 9 files changed, 21 insertions(+), 16 deletions(-) diff --git a/SwapMeter.c b/SwapMeter.c index 081967fe6..c0f482000 100644 --- a/SwapMeter.c +++ b/SwapMeter.c @@ -45,12 +45,12 @@ static void SwapMeter_display(const Object* cast, RichString* out) { RichString_writeAscii(out, CRT_colors[METER_TEXT], ":"); Meter_humanUnit(buffer, this->total, sizeof(buffer)); RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer); - Meter_humanUnit(buffer, this->values[0], sizeof(buffer)); + Meter_humanUnit(buffer, this->values[SWAP_METER_USED], sizeof(buffer)); RichString_appendAscii(out, CRT_colors[METER_TEXT], " used:"); RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer); - if (!isnan(this->values[1])) { - Meter_humanUnit(buffer, this->values[1], sizeof(buffer)); + if (!isnan(this->values[SWAP_METER_CACHE])) { + Meter_humanUnit(buffer, this->values[SWAP_METER_CACHE], sizeof(buffer)); RichString_appendAscii(out, CRT_colors[METER_TEXT], " cache:"); RichString_appendAscii(out, CRT_colors[SWAP_CACHE], buffer); } diff --git a/SwapMeter.h b/SwapMeter.h index 485485a19..b71e83fee 100644 --- a/SwapMeter.h +++ b/SwapMeter.h @@ -9,6 +9,11 @@ in the source distribution for its full text. #include "Meter.h" +typedef enum { + SWAP_METER_USED = 0, + SWAP_METER_CACHE = 1, + SWAP_METER_ITEMCOUNT = 2, // number of entries in this enum +} SwapMeterValues; extern const MeterClass SwapMeter_class; diff --git a/darwin/Platform.c b/darwin/Platform.c index e9d6caab1..38b1a8af4 100644 --- a/darwin/Platform.c +++ b/darwin/Platform.c @@ -278,7 +278,7 @@ void Platform_setSwapValues(Meter* mtr) { sysctl(mib, 2, &swapused, &swlen, NULL, 0); mtr->total = swapused.xsu_total / 1024; - mtr->values[0] = swapused.xsu_used / 1024; + mtr->values[SWAP_METER_USED] = swapused.xsu_used / 1024; } void Platform_setZfsArcValues(Meter* this) { diff --git a/dragonflybsd/Platform.c b/dragonflybsd/Platform.c index 944df990c..769a46eda 100644 --- a/dragonflybsd/Platform.c +++ b/dragonflybsd/Platform.c @@ -218,8 +218,8 @@ void Platform_setMemoryValues(Meter* this) { void Platform_setSwapValues(Meter* this) { const ProcessList* pl = this->pl; this->total = pl->totalSwap; - this->values[0] = pl->usedSwap; - this->values[1] = NAN; + this->values[SWAP_METER_USED] = pl->usedSwap; + this->values[SWAP_METER_CACHE] = NAN; } char* Platform_getProcessEnv(pid_t pid) { diff --git a/freebsd/Platform.c b/freebsd/Platform.c index 3f69d2072..e2f2509c8 100644 --- a/freebsd/Platform.c +++ b/freebsd/Platform.c @@ -250,8 +250,8 @@ void Platform_setMemoryValues(Meter* this) { void Platform_setSwapValues(Meter* this) { const ProcessList* pl = this->pl; this->total = pl->totalSwap; - this->values[0] = pl->usedSwap; - this->values[1] = NAN; + this->values[SWAP_METER_USED] = pl->usedSwap; + this->values[SWAP_METER_CACHE] = NAN; } void Platform_setZfsArcValues(Meter* this) { diff --git a/netbsd/Platform.c b/netbsd/Platform.c index e2f8c8800..785791894 100644 --- a/netbsd/Platform.c +++ b/netbsd/Platform.c @@ -280,8 +280,8 @@ void Platform_setMemoryValues(Meter* this) { void Platform_setSwapValues(Meter* this) { const ProcessList* pl = this->pl; this->total = pl->totalSwap; - this->values[0] = pl->usedSwap; - this->values[1] = NAN; + this->values[SWAP_METER_USED] = pl->usedSwap; + this->values[SWAP_METER_CACHE] = NAN; } char* Platform_getProcessEnv(pid_t pid) { diff --git a/openbsd/Platform.c b/openbsd/Platform.c index 74247484b..24e8aca1e 100644 --- a/openbsd/Platform.c +++ b/openbsd/Platform.c @@ -237,8 +237,8 @@ void Platform_setMemoryValues(Meter* this) { void Platform_setSwapValues(Meter* this) { const ProcessList* pl = this->pl; this->total = pl->totalSwap; - this->values[0] = pl->usedSwap; - this->values[1] = NAN; + this->values[SWAP_METER_USED] = pl->usedSwap; + this->values[SWAP_METER_CACHE] = NAN; } char* Platform_getProcessEnv(pid_t pid) { diff --git a/pcp/Platform.c b/pcp/Platform.c index d1bd0bc43..994cef3c6 100644 --- a/pcp/Platform.c +++ b/pcp/Platform.c @@ -548,8 +548,8 @@ void Platform_setMemoryValues(Meter* this) { void Platform_setSwapValues(Meter* this) { const ProcessList* pl = this->pl; this->total = pl->totalSwap; - this->values[0] = pl->usedSwap; - this->values[1] = pl->cachedSwap; + this->values[SWAP_METER_USED] = pl->usedSwap; + this->values[SWAP_METER_CACHE] = pl->cachedSwap; } void Platform_setZramValues(Meter* this) { diff --git a/solaris/Platform.c b/solaris/Platform.c index a99052c34..f3a386bc5 100644 --- a/solaris/Platform.c +++ b/solaris/Platform.c @@ -247,8 +247,8 @@ void Platform_setMemoryValues(Meter* this) { void Platform_setSwapValues(Meter* this) { const ProcessList* pl = this->pl; this->total = pl->totalSwap; - this->values[0] = pl->usedSwap; - this->values[1] = NAN; + this->values[SWAP_METER_USED] = pl->usedSwap; + this->values[SWAP_METER_CACHE] = NAN; } void Platform_setZfsArcValues(Meter* this) { From 490cedba04aef27c2a3c0bc3a9d6190b05082717 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= Date: Sat, 7 Jan 2023 18:25:24 +0100 Subject: [PATCH 025/106] Handle invalid process columns from configuration Skip over invalid process columns, such that later columns are displayed fine. Replace invalid sort key identifiers with the default sort column PID. --- Settings.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/Settings.c b/Settings.c index f1664d37a..abf8e6f22 100644 --- a/Settings.c +++ b/Settings.c @@ -262,10 +262,9 @@ static void ScreenSettings_readFields(ScreenSettings* ss, Hashtable* columns, co } int id = toFieldIndex(columns, ids[i]); if (id >= 0) - ss->fields[j] = id; + ss->fields[j++] = id; if (id > 0 && id < LAST_PROCESSFIELD) ss->flags |= Process_fields[id].flags; - j++; } String_freeArray(ids); } @@ -480,11 +479,15 @@ static bool Settings_read(Settings* this, const char* fileName, unsigned int ini } else if (strncmp(option[0], "screen:", 7) == 0) { screen = Settings_newScreen(this, &(const ScreenDefaults) { .name = option[0] + 7, .columns = option[1] }); } else if (String_eq(option[0], ".sort_key")) { - if (screen) - screen->sortKey = toFieldIndex(this->dynamicColumns, option[1]); + if (screen) { + int key = toFieldIndex(this->dynamicColumns, option[1]); + screen->sortKey = key > 0 ? key : PID; + } } else if (String_eq(option[0], ".tree_sort_key")) { - if (screen) - screen->treeSortKey = toFieldIndex(this->dynamicColumns, option[1]); + if (screen) { + int key = toFieldIndex(this->dynamicColumns, option[1]); + screen->treeSortKey = key > 0 ? key : PID; + } } else if (String_eq(option[0], ".sort_direction")) { if (screen) screen->direction = atoi(option[1]); From a05e1891594cbcdf813017aa3a56d6de044654de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= Date: Tue, 10 Jan 2023 19:04:49 +0100 Subject: [PATCH 026/106] Update the Panel header on sort order inversions On pressing 'I' to invert the current sort order redraw the panel header to update the order indicator. --- Action.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Action.c b/Action.c index 59322e25e..81432f5de 100644 --- a/Action.c +++ b/Action.c @@ -305,7 +305,7 @@ static Htop_Reaction actionLowerPriority(State* st) { static Htop_Reaction actionInvertSortOrder(State* st) { ScreenSettings_invertSortOrder(st->settings->ss); st->pl->needsSort = true; - return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING; + return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING | HTOP_UPDATE_PANELHDR; } static Htop_Reaction actionExpandOrCollapse(State* st) { From 4d40680aa04370b06e9a2acd732f0be2ee6b6eba Mon Sep 17 00:00:00 2001 From: Daniel Lange Date: Tue, 10 Jan 2023 23:35:09 +0100 Subject: [PATCH 027/106] Put a -dev release version into configure.ac again --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 37dba53a4..33e7fd887 100644 --- a/configure.ac +++ b/configure.ac @@ -6,7 +6,7 @@ # ---------------------------------------------------------------------- AC_PREREQ([2.69]) -AC_INIT([htop], [3.2.1], [htop@groups.io], [], [https://htop.dev/]) +AC_INIT([htop], [3.2.2-dev], [htop@groups.io], [], [https://htop.dev/]) AC_CONFIG_SRCDIR([htop.c]) AC_CONFIG_AUX_DIR([build-aux]) From 623ee31cfe3a39c87e6066c3044c7c38ce676e6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= Date: Sun, 23 May 2021 16:04:43 +0200 Subject: [PATCH 028/106] Add option to shadow path prefixes Shadow path prefixes which are used by distributions, like /usr/bin/ /usr/sbin/ /bin/ /sbin/ /usr/libexec/ --- DisplayOptionsPanel.c | 1 + Process.c | 70 +++++++++++++++++++++++++++++++++++++++++-- Process.h | 1 + Settings.c | 4 +++ Settings.h | 1 + 5 files changed, 75 insertions(+), 2 deletions(-) diff --git a/DisplayOptionsPanel.c b/DisplayOptionsPanel.c index 3b9e6cc43..f9fa9b12b 100644 --- a/DisplayOptionsPanel.c +++ b/DisplayOptionsPanel.c @@ -122,6 +122,7 @@ DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager* Panel_add(super, (Object*) CheckItem_newByRef("Show program path", &(settings->showProgramPath))); Panel_add(super, (Object*) CheckItem_newByRef("Highlight program \"basename\"", &(settings->highlightBaseName))); Panel_add(super, (Object*) CheckItem_newByRef("Highlight out-dated/removed programs (red) / libraries (yellow)", &(settings->highlightDeletedExe))); + Panel_add(super, (Object*) CheckItem_newByRef("Shadow distribution path prefixes", &(settings->shadowDistPathPrefix))); Panel_add(super, (Object*) CheckItem_newByRef("Merge exe, comm and cmdline in Command", &(settings->showMergedCommand))); Panel_add(super, (Object*) CheckItem_newByRef("- Try to find comm in cmdline (when Command is merged)", &(settings->findCommInCmdline))); Panel_add(super, (Object*) CheckItem_newByRef("- Try to strip exe from cmdline (when Command is merged)", &(settings->stripExeFromCmdline))); diff --git a/Process.c b/Process.c index ee7a5b39f..fcaa3d540 100644 --- a/Process.c +++ b/Process.c @@ -413,6 +413,7 @@ void Process_makeCommandStr(Process* this) { bool searchCommInCmdline = settings->findCommInCmdline; bool stripExeFromCmdline = settings->stripExeFromCmdline; bool showThreadNames = settings->showThreadNames; + bool shadowDistPathPrefix = settings->shadowDistPathPrefix; uint64_t settingsStamp = settings->lastUpdate; @@ -472,6 +473,56 @@ void Process_makeCommandStr(Process* this) { str = stpcpy(str, SEPARATOR); \ } while (0) + #define CHECK_AND_MARK(str_, prefix_) \ + if (String_startsWith(str_, prefix_)) { \ + WRITE_HIGHLIGHT(0, strlen(prefix_), CRT_colors[PROCESS_SHADOW], CMDLINE_HIGHLIGHT_FLAG_PREFIXDIR); \ + break; \ + } else (void)0 + + #define CHECK_AND_MARK_DIST_PATH_PREFIXES(str_) \ + do { \ + if ((str_)[0] != '/') { \ + break; \ + } \ + switch ((str_)[1]) { \ + case 'b': \ + CHECK_AND_MARK(str_, "/bin/"); \ + break; \ + case 'l': \ + CHECK_AND_MARK(str_, "/lib/"); \ + CHECK_AND_MARK(str_, "/lib32/"); \ + CHECK_AND_MARK(str_, "/lib64/"); \ + CHECK_AND_MARK(str_, "/libx32/"); \ + break; \ + case 's': \ + CHECK_AND_MARK(str_, "/sbin/"); \ + break; \ + case 'u': \ + if (String_startsWith(str_, "/usr/")) { \ + switch ((str_)[5]) { \ + case 'b': \ + CHECK_AND_MARK(str_, "/usr/bin/"); \ + break; \ + case 'l': \ + CHECK_AND_MARK(str_, "/usr/libexec/"); \ + CHECK_AND_MARK(str_, "/usr/lib/"); \ + CHECK_AND_MARK(str_, "/usr/lib32/"); \ + CHECK_AND_MARK(str_, "/usr/lib64/"); \ + CHECK_AND_MARK(str_, "/usr/libx32/"); \ + \ + CHECK_AND_MARK(str_, "/usr/local/bin/"); \ + CHECK_AND_MARK(str_, "/usr/local/lib/"); \ + CHECK_AND_MARK(str_, "/usr/local/sbin/"); \ + break; \ + case 's': \ + CHECK_AND_MARK(str_, "/usr/sbin/"); \ + break; \ + } \ + } \ + break; \ + } \ + } while (0) + const int baseAttr = Process_isThread(this) ? CRT_colors[PROCESS_THREAD_BASENAME] : CRT_colors[PROCESS_BASENAME]; const int commAttr = Process_isThread(this) ? CRT_colors[PROCESS_THREAD_COMM] : CRT_colors[PROCESS_COMM]; const int delExeAttr = CRT_colors[FAILED_READ]; @@ -510,6 +561,9 @@ void Process_makeCommandStr(Process* this) { } } + if (shadowDistPathPrefix && showProgramPath) + CHECK_AND_MARK_DIST_PATH_PREFIXES(cmdline); + if (cmdlineBasenameEnd > cmdlineBasenameStart) WRITE_HIGHLIGHT(showProgramPath ? cmdlineBasenameStart : 0, cmdlineBasenameEnd - cmdlineBasenameStart, baseAttr, CMDLINE_HIGHLIGHT_FLAG_BASENAME); @@ -537,6 +591,8 @@ void Process_makeCommandStr(Process* this) { /* Start with copying exe */ if (showProgramPath) { + if (shadowDistPathPrefix) + CHECK_AND_MARK_DIST_PATH_PREFIXES(procExe); if (haveCommInExe) WRITE_HIGHLIGHT(exeBasenameOffset, exeBasenameLen, commAttr, CMDLINE_HIGHLIGHT_FLAG_COMM); WRITE_HIGHLIGHT(exeBasenameOffset, exeBasenameLen, baseAttr, CMDLINE_HIGHLIGHT_FLAG_BASENAME); @@ -594,6 +650,9 @@ void Process_makeCommandStr(Process* this) { WRITE_SEPARATOR; } + if (shadowDistPathPrefix) + CHECK_AND_MARK_DIST_PATH_PREFIXES(cmdline); + if (!haveCommInExe && haveCommInCmdline && !haveCommField && (!Process_isUserlandThread(this) || showThreadNames)) WRITE_HIGHLIGHT(commStart, commEnd - commStart, commAttr, CMDLINE_HIGHLIGHT_FLAG_COMM); @@ -601,6 +660,8 @@ void Process_makeCommandStr(Process* this) { if (*cmdline) (void)stpcpyWithNewlineConversion(str, cmdline); + #undef CHECK_AND_MARK_DIST_PATH_PREFIXES + #undef CHECK_AND_MARK #undef WRITE_SEPARATOR #undef WRITE_HIGHLIGHT } @@ -609,6 +670,7 @@ void Process_writeCommand(const Process* this, int attr, int baseAttr, RichStrin (void)baseAttr; const ProcessMergedCommand* mc = &this->mergedCommand; + const char* mergedCommand = mc->str; int strStart = RichString_size(str); @@ -616,7 +678,7 @@ void Process_writeCommand(const Process* this, int attr, int baseAttr, RichStrin const bool highlightSeparator = true; const bool highlightDeleted = this->settings->highlightDeletedExe; - if (!this->mergedCommand.str) { + if (!mergedCommand) { int len = 0; const char* cmdline = this->cmdline; @@ -649,7 +711,7 @@ void Process_writeCommand(const Process* this, int attr, int baseAttr, RichStrin return; } - RichString_appendWide(str, attr, this->mergedCommand.str); + RichString_appendWide(str, attr, mergedCommand); for (size_t i = 0, hlCount = CLAMP(mc->highlightCount, 0, ARRAYSIZE(mc->highlights)); i < hlCount; i++) { const ProcessCmdlineHighlight* hl = &mc->highlights[i]; @@ -669,6 +731,10 @@ void Process_writeCommand(const Process* this, int attr, int baseAttr, RichStrin if (!highlightDeleted) continue; + if (hl->flags & CMDLINE_HIGHLIGHT_FLAG_PREFIXDIR) + if (!highlightDeleted) + continue; + RichString_setAttrn(str, hl->attr, strStart + hl->offset, hl->length); } } diff --git a/Process.h b/Process.h index fa047ace9..eb79470d3 100644 --- a/Process.h +++ b/Process.h @@ -331,6 +331,7 @@ static inline bool Process_isThread(const Process* this) { #define CMDLINE_HIGHLIGHT_FLAG_BASENAME 0x00000002 #define CMDLINE_HIGHLIGHT_FLAG_COMM 0x00000004 #define CMDLINE_HIGHLIGHT_FLAG_DELETED 0x00000008 +#define CMDLINE_HIGHLIGHT_FLAG_PREFIXDIR 0x00000010 #define ONE_K 1024UL #define ONE_M (ONE_K * ONE_K) diff --git a/Settings.c b/Settings.c index abf8e6f22..8543b9e43 100644 --- a/Settings.c +++ b/Settings.c @@ -393,6 +393,8 @@ static bool Settings_read(Settings* this, const char* fileName, unsigned int ini this->highlightBaseName = atoi(option[1]); } else if (String_eq(option[0], "highlight_deleted_exe")) { this->highlightDeletedExe = atoi(option[1]); + } else if (String_eq(option[0], "shadow_distribution_path_prefix")) { + this->shadowDistPathPrefix = atoi(option[1]); } else if (String_eq(option[0], "highlight_megabytes")) { this->highlightMegabytes = atoi(option[1]); } else if (String_eq(option[0], "highlight_threads")) { @@ -587,6 +589,7 @@ int Settings_write(const Settings* this, bool onCrash) { printSettingInteger("show_program_path", this->showProgramPath); printSettingInteger("highlight_base_name", this->highlightBaseName); printSettingInteger("highlight_deleted_exe", this->highlightDeletedExe); + printSettingInteger("shadow_distribution_path_prefix", this->shadowDistPathPrefix); printSettingInteger("highlight_megabytes", this->highlightMegabytes); printSettingInteger("highlight_threads", this->highlightThreads); printSettingInteger("highlight_changes", this->highlightChanges); @@ -678,6 +681,7 @@ Settings* Settings_new(unsigned int initialCpuCount, Hashtable* dynamicColumns) this->hideRunningInContainer = false; this->highlightBaseName = false; this->highlightDeletedExe = true; + this->shadowDistPathPrefix = false; this->highlightMegabytes = true; this->detailedCPUTime = false; this->countCPUsFromOne = false; diff --git a/Settings.h b/Settings.h index f6a6ea653..baf05da3b 100644 --- a/Settings.h +++ b/Settings.h @@ -77,6 +77,7 @@ typedef struct Settings_ { bool hideUserlandThreads; bool highlightBaseName; bool highlightDeletedExe; + bool shadowDistPathPrefix; bool highlightMegabytes; bool highlightThreads; bool highlightChanges; From c9f654da96f9af05d599bc778575dd5b71a2c5ae Mon Sep 17 00:00:00 2001 From: Nathan Scott Date: Sat, 4 Feb 2023 11:45:55 +1100 Subject: [PATCH 029/106] Fix an incorrect memset pointer in PCP platform CPU handling --- pcp/PCPProcessList.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pcp/PCPProcessList.c b/pcp/PCPProcessList.c index 045f7ae02..f893689a1 100644 --- a/pcp/PCPProcessList.c +++ b/pcp/PCPProcessList.c @@ -571,7 +571,7 @@ static void PCPProcessList_updateAllCPUTime(PCPProcessList* this, PCPMetric metr { pmAtomValue* value = &this->cpu[cpumetric]; if (PCPMetric_values(metric, value, 1, PM_TYPE_U64) == NULL) - memset(&value, 0, sizeof(pmAtomValue)); + memset(value, 0, sizeof(pmAtomValue)); } static void PCPProcessList_updatePerCPUTime(PCPProcessList* this, PCPMetric metric, CPUMetric cpumetric) From ca7e92bd78368befa88a3a6d732d1595c388c660 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= Date: Sat, 4 Feb 2023 18:03:17 +0100 Subject: [PATCH 030/106] Use strict function prototypes also for defintions freebsd/Platform.c:151:23: error: a function declaration without a prototype is deprecated in all versions of C [-Werror,-Wstrict-prototypes] int Platform_getUptime() { ^ void --- darwin/Platform.c | 4 ++-- darwin/PlatformHelpers.c | 4 ++-- dragonflybsd/Platform.c | 4 ++-- freebsd/Platform.c | 4 ++-- netbsd/Platform.c | 4 ++-- openbsd/Platform.c | 4 ++-- solaris/Platform.c | 4 ++-- unsupported/Platform.c | 4 ++-- 8 files changed, 16 insertions(+), 16 deletions(-) diff --git a/darwin/Platform.c b/darwin/Platform.c index 38b1a8af4..332752b26 100644 --- a/darwin/Platform.c +++ b/darwin/Platform.c @@ -172,7 +172,7 @@ void Platform_setBindings(Htop_Action* keys) { (void) keys; } -int Platform_getUptime() { +int Platform_getUptime(void) { struct timeval bootTime, currTime; int mib[2] = { CTL_KERN, KERN_BOOTTIME }; size_t size = sizeof(bootTime); @@ -200,7 +200,7 @@ void Platform_getLoadAverage(double* one, double* five, double* fifteen) { } } -int Platform_getMaxPid() { +int Platform_getMaxPid(void) { /* http://opensource.apple.com/source/xnu/xnu-2782.1.97/bsd/sys/proc_internal.hh */ return 99999; } diff --git a/darwin/PlatformHelpers.c b/darwin/PlatformHelpers.c index 1b9a246d6..97f0741a1 100644 --- a/darwin/PlatformHelpers.c +++ b/darwin/PlatformHelpers.c @@ -69,7 +69,7 @@ void Platform_getCPUBrandString(char* cpuBrandString, size_t cpuBrandStringSize) } // Adapted from https://developer.apple.com/documentation/apple-silicon/about-the-rosetta-translation-environment -bool Platform_isRunningTranslated() { +bool Platform_isRunningTranslated(void) { int ret = 0; size_t size = sizeof(ret); errno = 0; @@ -87,7 +87,7 @@ bool Platform_isRunningTranslated() { return ret; } -double Platform_calculateNanosecondsPerMachTick() { +double Platform_calculateNanosecondsPerMachTick(void) { // Check if we can determine the timebase used on this system. // If the API is unavailable assume we get our timebase in nanoseconds. #ifndef HAVE_MACH_TIMEBASE_INFO diff --git a/dragonflybsd/Platform.c b/dragonflybsd/Platform.c index 769a46eda..8a684d866 100644 --- a/dragonflybsd/Platform.c +++ b/dragonflybsd/Platform.c @@ -126,7 +126,7 @@ void Platform_setBindings(Htop_Action* keys) { (void) keys; } -int Platform_getUptime() { +int Platform_getUptime(void) { struct timeval bootTime, currTime; int mib[2] = { CTL_KERN, KERN_BOOTTIME }; size_t size = sizeof(bootTime); @@ -157,7 +157,7 @@ void Platform_getLoadAverage(double* one, double* five, double* fifteen) { *fifteen = (double) loadAverage.ldavg[2] / loadAverage.fscale; } -int Platform_getMaxPid() { +int Platform_getMaxPid(void) { int maxPid; size_t size = sizeof(maxPid); int err = sysctlbyname("kern.pid_max", &maxPid, &size, NULL, 0); diff --git a/freebsd/Platform.c b/freebsd/Platform.c index e2f2509c8..38d06169a 100644 --- a/freebsd/Platform.c +++ b/freebsd/Platform.c @@ -148,7 +148,7 @@ void Platform_setBindings(Htop_Action* keys) { (void) keys; } -int Platform_getUptime() { +int Platform_getUptime(void) { struct timeval bootTime, currTime; const int mib[2] = { CTL_KERN, KERN_BOOTTIME }; size_t size = sizeof(bootTime); @@ -179,7 +179,7 @@ void Platform_getLoadAverage(double* one, double* five, double* fifteen) { *fifteen = (double) loadAverage.ldavg[2] / loadAverage.fscale; } -int Platform_getMaxPid() { +int Platform_getMaxPid(void) { int maxPid; size_t size = sizeof(maxPid); int err = sysctlbyname("kern.pid_max", &maxPid, &size, NULL, 0); diff --git a/netbsd/Platform.c b/netbsd/Platform.c index 785791894..ad6050c00 100644 --- a/netbsd/Platform.c +++ b/netbsd/Platform.c @@ -196,7 +196,7 @@ void Platform_setBindings(Htop_Action* keys) { (void) keys; } -int Platform_getUptime() { +int Platform_getUptime(void) { struct timeval bootTime, currTime; const int mib[2] = { CTL_KERN, KERN_BOOTTIME }; size_t size = sizeof(bootTime); @@ -227,7 +227,7 @@ void Platform_getLoadAverage(double* one, double* five, double* fifteen) { *fifteen = (double) loadAverage.ldavg[2] / loadAverage.fscale; } -int Platform_getMaxPid() { +int Platform_getMaxPid(void) { // https://nxr.netbsd.org/xref/src/sys/sys/ansi.h#__pid_t // pid is assigned as a 32bit Integer. return INT32_MAX; diff --git a/openbsd/Platform.c b/openbsd/Platform.c index 24e8aca1e..1ce5ba194 100644 --- a/openbsd/Platform.c +++ b/openbsd/Platform.c @@ -143,7 +143,7 @@ void Platform_setBindings(Htop_Action* keys) { (void) keys; } -int Platform_getUptime() { +int Platform_getUptime(void) { struct timeval bootTime, currTime; const int mib[2] = { CTL_KERN, KERN_BOOTTIME }; size_t size = sizeof(bootTime); @@ -174,7 +174,7 @@ void Platform_getLoadAverage(double* one, double* five, double* fifteen) { *fifteen = (double) loadAverage.ldavg[2] / loadAverage.fscale; } -int Platform_getMaxPid() { +int Platform_getMaxPid(void) { return 2 * THREAD_PID_OFFSET; } diff --git a/solaris/Platform.c b/solaris/Platform.c index f3a386bc5..96f35263d 100644 --- a/solaris/Platform.c +++ b/solaris/Platform.c @@ -144,7 +144,7 @@ void Platform_setBindings(Htop_Action* keys) { (void) keys; } -int Platform_getUptime() { +int Platform_getUptime(void) { int boot_time = 0; int curr_time = time(NULL); struct utmpx* ent; @@ -173,7 +173,7 @@ void Platform_getLoadAverage(double* one, double* five, double* fifteen) { *fifteen = plat_loadavg[LOADAVG_15MIN]; } -int Platform_getMaxPid() { +int Platform_getMaxPid(void) { int vproc = 32778; // Reasonable Solaris default kstat_ctl_t* kc = kstat_open(); diff --git a/unsupported/Platform.c b/unsupported/Platform.c index fcccb21a9..27bc560bd 100644 --- a/unsupported/Platform.c +++ b/unsupported/Platform.c @@ -90,7 +90,7 @@ void Platform_setBindings(Htop_Action* keys) { (void) keys; } -int Platform_getUptime() { +int Platform_getUptime(void) { return 0; } @@ -100,7 +100,7 @@ void Platform_getLoadAverage(double* one, double* five, double* fifteen) { *fifteen = 0; } -int Platform_getMaxPid() { +int Platform_getMaxPid(void) { return 1; } From 0eb54384593a0ae5944731e0e7cd527970b05b73 Mon Sep 17 00:00:00 2001 From: er-azh <80633916+er-azh@users.noreply.github.com> Date: Sat, 4 Feb 2023 15:56:06 +0330 Subject: [PATCH 031/106] Implement shared memory for FreeBSD --- freebsd/FreeBSDProcessList.c | 10 ++++++++++ freebsd/Platform.c | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/freebsd/FreeBSDProcessList.c b/freebsd/FreeBSDProcessList.c index 2575d8fe9..b7643222a 100644 --- a/freebsd/FreeBSDProcessList.c +++ b/freebsd/FreeBSDProcessList.c @@ -25,6 +25,7 @@ in the source distribution for its full text. #include #include #include +#include #include "CRT.h" #include "Compat.h" @@ -49,6 +50,7 @@ static int MIB_vm_stats_vm_v_active_count[4]; static int MIB_vm_stats_vm_v_cache_count[4]; static int MIB_vm_stats_vm_v_inactive_count[4]; static int MIB_vm_stats_vm_v_free_count[4]; +static int MIB_vm_vmtotal[2]; static int MIB_vfs_bufspace[2]; @@ -82,6 +84,9 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, H len = 4; sysctlnametomib("vm.stats.vm.v_cache_count", MIB_vm_stats_vm_v_cache_count, &len); len = 4; sysctlnametomib("vm.stats.vm.v_inactive_count", MIB_vm_stats_vm_v_inactive_count, &len); len = 4; sysctlnametomib("vm.stats.vm.v_free_count", MIB_vm_stats_vm_v_free_count, &len); + MIB_vm_vmtotal[0] = CTL_VM; + MIB_vm_vmtotal[1] = VM_TOTAL; + len = 2; sysctlnametomib("vfs.bufspace", MIB_vfs_bufspace, &len); @@ -330,6 +335,7 @@ static inline void FreeBSDProcessList_scanMemoryInfo(ProcessList* pl) { u_int memActive, memWire, cachedMem; long buffersMem; size_t len; + struct vmtotal vmtotal; //disabled for now, as it is always smaller than phycal amount of memory... //...to avoid "where is my memory?" questions @@ -360,6 +366,10 @@ static inline void FreeBSDProcessList_scanMemoryInfo(ProcessList* pl) { cachedMem *= pageSizeKb; pl->cachedMem = cachedMem; + len = sizeof(vmtotal); + sysctl(MIB_vm_vmtotal, 2, &(vmtotal), &len, NULL, 0); + pl->sharedMem = vmtotal.t_vmshr * pageSizeKb; + if (fpl->zfs.enabled) { fpl->memWire -= fpl->zfs.size; pl->cachedMem += fpl->zfs.size; diff --git a/freebsd/Platform.c b/freebsd/Platform.c index 38d06169a..646163af1 100644 --- a/freebsd/Platform.c +++ b/freebsd/Platform.c @@ -232,7 +232,7 @@ void Platform_setMemoryValues(Meter* this) { this->total = pl->totalMem; this->values[MEMORY_METER_USED] = pl->usedMem; this->values[MEMORY_METER_BUFFERS] = pl->buffersMem; - // this->values[MEMORY_METER_SHARED] = "shared memory, like tmpfs and shm" + this->values[MEMORY_METER_SHARED] = pl->sharedMem; this->values[MEMORY_METER_CACHE] = pl->cachedMem; // this->values[MEMORY_METER_AVAILABLE] = "available memory" From 17c13244c2a314b2d482e4bdc36dffbab1dd6264 Mon Sep 17 00:00:00 2001 From: er-azh <80633916+er-azh@users.noreply.github.com> Date: Sat, 4 Feb 2023 17:21:54 +0330 Subject: [PATCH 032/106] Use sysctlnametomib for vm.vmtotal --- freebsd/FreeBSDProcessList.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/freebsd/FreeBSDProcessList.c b/freebsd/FreeBSDProcessList.c index b7643222a..2c7e20b29 100644 --- a/freebsd/FreeBSDProcessList.c +++ b/freebsd/FreeBSDProcessList.c @@ -84,9 +84,7 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, H len = 4; sysctlnametomib("vm.stats.vm.v_cache_count", MIB_vm_stats_vm_v_cache_count, &len); len = 4; sysctlnametomib("vm.stats.vm.v_inactive_count", MIB_vm_stats_vm_v_inactive_count, &len); len = 4; sysctlnametomib("vm.stats.vm.v_free_count", MIB_vm_stats_vm_v_free_count, &len); - MIB_vm_vmtotal[0] = CTL_VM; - MIB_vm_vmtotal[1] = VM_TOTAL; - + len = 2; sysctlnametomib("vm.vmtotal", MIB_vm_vmtotal, &len); len = 2; sysctlnametomib("vfs.bufspace", MIB_vfs_bufspace, &len); From b728d96fb68bd6bb2e5c3030e8678b2702191937 Mon Sep 17 00:00:00 2001 From: Nathan Scott Date: Sat, 4 Feb 2023 12:14:47 +1100 Subject: [PATCH 033/106] Update changelog in preparation for htop-3.2.2 release --- ChangeLog | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/ChangeLog b/ChangeLog index 8f5266cbc..5717a52ef 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,43 @@ +What's new in version 3.2.2 + +* CPUMeter now can show frequency in text mode +* Add option to render distribution path prefixes shadowed +* DiskIOMeter converts to bytes per second (not per interval) +* DiskIOMeter uses complete units, including missing "iB/s" +* DiskIOMeter indicates read and write in meter mode +* NetworkIOMeter converts to packets per second, shows packet rate +* Allow continued process following when changing display settings +* Update the panel header when changing to another tab +* Drop margin around the header if there are no meters +* Use Unicode replacement character for non-printable characters +* Default color preset uses bold blue for better visibility +* Update the Panel header on sort order inversions ('I') +* Toggle the header meters with pound key +* Fix ScreenPanel to handle quitting the panel while renaming +* Add fallback for HOME environment variable using passwd database +* Replace meaningless ID column with FD column in lock screen +* Use device format in the lock screen matching the files screen +* On Linux, improvements to file-descriptor lock detection +* On Linux, further distinguish systemd states in the SystemdMeter +* On Linux, improvements to cgroup and container identification +* On Linux, support openat(2) without readlinkat(2) platforms +* On Darwin, fix current process buffer handling for busy systems +* On DragonFly BSD, fix incorrect processor time of processes +* On FreeBSD, fix an issue with the memory graph not showing correctly +* On FreeBSD, add support for displaying shared memory usage +* On PCP, use pmLookupDescs(3) if available for efficiency +* On PCP, normalize generic columns values for consistent display +* On PCP, changes preparing for configurable, dynamic screens +* Handle invalid process columns from the configuration file +* Avoid undefined behaviour with deeply nested processes +* Fix crash when removing the currently active screen +* Prevent possible crash on a very early error path +* Include automake for Debian/Ubuntu +* Restore non-mouse support +* Reject unsupported command line arguments +* Document idle process state +* Clarify M_TRS/M_DRS columns + What's new in version 3.2.1 * Fix setting to show all branches collapsed by default From 55c10eccd71b6b016a50237bc8c8508b7bf66783 Mon Sep 17 00:00:00 2001 From: Nathan Scott Date: Sun, 5 Feb 2023 09:26:23 +1100 Subject: [PATCH 034/106] Drop -dev version suffix in configure.ac for release --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 33e7fd887..bbb042ac3 100644 --- a/configure.ac +++ b/configure.ac @@ -6,7 +6,7 @@ # ---------------------------------------------------------------------- AC_PREREQ([2.69]) -AC_INIT([htop], [3.2.2-dev], [htop@groups.io], [], [https://htop.dev/]) +AC_INIT([htop], [3.2.2], [htop@groups.io], [], [https://htop.dev/]) AC_CONFIG_SRCDIR([htop.c]) AC_CONFIG_AUX_DIR([build-aux]) From 9d16d0a1cc29c4103afaef9c9e8e9e1db73c02db Mon Sep 17 00:00:00 2001 From: Nathan Scott Date: Sun, 5 Feb 2023 09:32:28 +1100 Subject: [PATCH 035/106] Update configure.ac for next planned release version --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index bbb042ac3..de0718e14 100644 --- a/configure.ac +++ b/configure.ac @@ -6,7 +6,7 @@ # ---------------------------------------------------------------------- AC_PREREQ([2.69]) -AC_INIT([htop], [3.2.2], [htop@groups.io], [], [https://htop.dev/]) +AC_INIT([htop], [3.3.0-dev], [htop@groups.io], [], [https://htop.dev/]) AC_CONFIG_SRCDIR([htop.c]) AC_CONFIG_AUX_DIR([build-aux]) From 03be9d15a340088ab728449ef2f0b58bec84ac8d Mon Sep 17 00:00:00 2001 From: er-azh <80633916+er-azh@users.noreply.github.com> Date: Thu, 2 Feb 2023 09:27:38 +0330 Subject: [PATCH 036/106] Ignore NAN values when drawing graphs --- Meter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Meter.c b/Meter.c index 164c4d380..7f634f19a 100644 --- a/Meter.c +++ b/Meter.c @@ -325,7 +325,7 @@ static void GraphMeterMode_draw(Meter* this, int x, int y, int w) { double value = 0.0; for (uint8_t i = 0; i < this->curItems; i++) - value += this->values[i]; + value += !isnan(this->values[i]) ? this->values[i] : 0; data->values[nValues - 1] = value; } From 4e8ec3cac9606b8f64c101c9136e846618dff7b6 Mon Sep 17 00:00:00 2001 From: er-azh <80633916+er-azh@users.noreply.github.com> Date: Thu, 2 Feb 2023 10:53:36 +0330 Subject: [PATCH 037/106] FreeBSD: remove duplicate zfs ARC size subtraction --- freebsd/FreeBSDProcessList.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/freebsd/FreeBSDProcessList.c b/freebsd/FreeBSDProcessList.c index 2c7e20b29..9ee5ea402 100644 --- a/freebsd/FreeBSDProcessList.c +++ b/freebsd/FreeBSDProcessList.c @@ -368,11 +368,6 @@ static inline void FreeBSDProcessList_scanMemoryInfo(ProcessList* pl) { sysctl(MIB_vm_vmtotal, 2, &(vmtotal), &len, NULL, 0); pl->sharedMem = vmtotal.t_vmshr * pageSizeKb; - if (fpl->zfs.enabled) { - fpl->memWire -= fpl->zfs.size; - pl->cachedMem += fpl->zfs.size; - } - pl->usedMem = fpl->memActive + fpl->memWire; struct kvm_swap swap[16]; From f1da8cfa28cce46cc7a4ab1661be35e2173155ab Mon Sep 17 00:00:00 2001 From: Aidan Epstein Date: Sun, 27 Nov 2022 19:51:42 -0800 Subject: [PATCH 038/106] Add a systemd user meter to monitor user units. --- linux/Platform.c | 1 + linux/SystemdMeter.c | 173 +++++++++++++++++++++++++++---------------- linux/SystemdMeter.h | 2 + 3 files changed, 112 insertions(+), 64 deletions(-) diff --git a/linux/Platform.c b/linux/Platform.c index 64f25c420..12852e36b 100644 --- a/linux/Platform.c +++ b/linux/Platform.c @@ -249,6 +249,7 @@ const MeterClass* const Platform_meterTypes[] = { &NetworkIOMeter_class, &SELinuxMeter_class, &SystemdMeter_class, + &SystemdUserMeter_class, NULL }; diff --git a/linux/SystemdMeter.c b/linux/SystemdMeter.c index cee323189..848d1e4ef 100644 --- a/linux/SystemdMeter.c +++ b/linux/SystemdMeter.c @@ -9,6 +9,7 @@ in the source distribution for its full text. #include #include +#include #include #include #include @@ -39,6 +40,7 @@ in the source distribution for its full text. typedef void sd_bus; typedef void sd_bus_error; static int (*sym_sd_bus_open_system)(sd_bus**); +static int (*sym_sd_bus_open_user)(sd_bus**); static int (*sym_sd_bus_get_property_string)(sd_bus*, const char*, const char*, const char*, const char*, sd_bus_error*, char**); static int (*sym_sd_bus_get_property_trivial)(sd_bus*, const char*, const char*, const char*, const char*, sd_bus_error*, char, void*); static sd_bus* (*sym_sd_bus_unref)(sd_bus*); @@ -46,37 +48,43 @@ static void* dlopenHandle = NULL; #endif /* BUILD_STATIC */ -#if !defined(BUILD_STATIC) || defined(HAVE_LIBSYSTEMD) -static sd_bus* bus = NULL; -#endif /* !BUILD_STATIC || HAVE_LIBSYSTEMD */ - #define INVALID_VALUE ((unsigned int)-1) -static char* systemState = NULL; -static unsigned int nFailedUnits = INVALID_VALUE; -static unsigned int nInstalledJobs = INVALID_VALUE; -static unsigned int nNames = INVALID_VALUE; -static unsigned int nJobs = INVALID_VALUE; +typedef struct SystemdMeterContext { +#if !defined(BUILD_STATIC) || defined(HAVE_LIBSYSTEMD) + sd_bus* bus; +#endif /* !BUILD_STATIC || HAVE_LIBSYSTEMD */ + char* systemState; + unsigned int nFailedUnits; + unsigned int nInstalledJobs; + unsigned int nNames; + unsigned int nJobs; +} SystemdMeterContext_t; + +static SystemdMeterContext_t ctx_system; +static SystemdMeterContext_t ctx_user; static void SystemdMeter_done(ATTR_UNUSED Meter* this) { - free(systemState); - systemState = NULL; + SystemdMeterContext_t* ctx = String_eq(Meter_name(this), "SystemdUser") ? &ctx_user : &ctx_system; + + free(ctx->systemState); + ctx->systemState = NULL; #ifdef BUILD_STATIC # ifdef HAVE_LIBSYSTEMD - if (bus) { - sym_sd_bus_unref(bus); + if (ctx->bus) { + sym_sd_bus_unref(ctx->bus); } - bus = NULL; + ctx->bus = NULL; # endif /* HAVE_LIBSYSTEMD */ #else /* BUILD_STATIC */ - if (bus && dlopenHandle) { - sym_sd_bus_unref(bus); + if (ctx->bus && dlopenHandle) { + sym_sd_bus_unref(ctx->bus); } - bus = NULL; + ctx->bus = NULL; - if (dlopenHandle) { + if (!ctx_system.systemState && !ctx_user.systemState && dlopenHandle) { dlclose(dlopenHandle); dlopenHandle = NULL; } @@ -84,7 +92,8 @@ static void SystemdMeter_done(ATTR_UNUSED Meter* this) { } #if !defined(BUILD_STATIC) || defined(HAVE_LIBSYSTEMD) -static int updateViaLib(void) { +static int updateViaLib(bool user) { + SystemdMeterContext_t* ctx = user ? &ctx_user : &ctx_system; #ifndef BUILD_STATIC if (!dlopenHandle) { dlopenHandle = dlopen("libsystemd.so.0", RTLD_LAZY); @@ -101,6 +110,7 @@ static int updateViaLib(void) { } while(0) resolve(sd_bus_open_system); + resolve(sd_bus_open_user); resolve(sd_bus_get_property_string); resolve(sd_bus_get_property_trivial); resolve(sd_bus_unref); @@ -110,10 +120,13 @@ static int updateViaLib(void) { #endif /* !BUILD_STATIC */ int r; - /* Connect to the system bus */ - if (!bus) { - r = sym_sd_bus_open_system(&bus); + if (!ctx->bus) { + if (user) { + r = sym_sd_bus_open_user(&ctx->bus); + } else { + r = sym_sd_bus_open_system(&ctx->bus); + } if (r < 0) goto busfailure; } @@ -122,57 +135,57 @@ static int updateViaLib(void) { static const char* const busObjectPath = "/org/freedesktop/systemd1"; static const char* const busInterfaceName = "org.freedesktop.systemd1.Manager"; - r = sym_sd_bus_get_property_string(bus, + r = sym_sd_bus_get_property_string(ctx->bus, busServiceName, /* service to contact */ busObjectPath, /* object path */ busInterfaceName, /* interface name */ "SystemState", /* property name */ NULL, /* object to return error in */ - &systemState); + &ctx->systemState); if (r < 0) goto busfailure; - r = sym_sd_bus_get_property_trivial(bus, + r = sym_sd_bus_get_property_trivial(ctx->bus, busServiceName, /* service to contact */ busObjectPath, /* object path */ busInterfaceName, /* interface name */ "NFailedUnits", /* property name */ NULL, /* object to return error in */ 'u', /* property type */ - &nFailedUnits); + &ctx->nFailedUnits); if (r < 0) goto busfailure; - r = sym_sd_bus_get_property_trivial(bus, + r = sym_sd_bus_get_property_trivial(ctx->bus, busServiceName, /* service to contact */ busObjectPath, /* object path */ busInterfaceName, /* interface name */ "NInstalledJobs", /* property name */ NULL, /* object to return error in */ 'u', /* property type */ - &nInstalledJobs); + &ctx->nInstalledJobs); if (r < 0) goto busfailure; - r = sym_sd_bus_get_property_trivial(bus, + r = sym_sd_bus_get_property_trivial(ctx->bus, busServiceName, /* service to contact */ busObjectPath, /* object path */ busInterfaceName, /* interface name */ "NNames", /* property name */ NULL, /* object to return error in */ 'u', /* property type */ - &nNames); + &ctx->nNames); if (r < 0) goto busfailure; - r = sym_sd_bus_get_property_trivial(bus, + r = sym_sd_bus_get_property_trivial(ctx->bus, busServiceName, /* service to contact */ busObjectPath, /* object path */ busInterfaceName, /* interface name */ "NJobs", /* property name */ NULL, /* object to return error in */ 'u', /* property type */ - &nJobs); + &ctx->nJobs); if (r < 0) goto busfailure; @@ -180,8 +193,8 @@ static int updateViaLib(void) { return 0; busfailure: - sym_sd_bus_unref(bus); - bus = NULL; + sym_sd_bus_unref(ctx->bus); + ctx->bus = NULL; return -2; #ifndef BUILD_STATIC @@ -195,7 +208,9 @@ static int updateViaLib(void) { } #endif /* !BUILD_STATIC || HAVE_LIBSYSTEMD */ -static void updateViaExec(void) { +static void updateViaExec(bool user) { + SystemdMeterContext_t* ctx = user ? &ctx_user : &ctx_system; + if (Settings_isReadonly()) return; @@ -225,6 +240,7 @@ static void updateViaExec(void) { "systemctl", "systemctl", "show", + user ? "--user" : "--system", "--property=SystemState", "--property=NFailedUnits", "--property=NNames", @@ -254,15 +270,15 @@ static void updateViaExec(void) { if (newline) { *newline = '\0'; } - free_and_xStrdup(&systemState, lineBuffer + strlen("SystemState=")); + free_and_xStrdup(&ctx->systemState, lineBuffer + strlen("SystemState=")); } else if (String_startsWith(lineBuffer, "NFailedUnits=")) { - nFailedUnits = strtoul(lineBuffer + strlen("NFailedUnits="), NULL, 10); + ctx->nFailedUnits = strtoul(lineBuffer + strlen("NFailedUnits="), NULL, 10); } else if (String_startsWith(lineBuffer, "NNames=")) { - nNames = strtoul(lineBuffer + strlen("NNames="), NULL, 10); + ctx->nNames = strtoul(lineBuffer + strlen("NNames="), NULL, 10); } else if (String_startsWith(lineBuffer, "NJobs=")) { - nJobs = strtoul(lineBuffer + strlen("NJobs="), NULL, 10); + ctx->nJobs = strtoul(lineBuffer + strlen("NJobs="), NULL, 10); } else if (String_startsWith(lineBuffer, "NInstalledJobs=")) { - nInstalledJobs = strtoul(lineBuffer + strlen("NInstalledJobs="), NULL, 10); + ctx->nInstalledJobs = strtoul(lineBuffer + strlen("NInstalledJobs="), NULL, 10); } } @@ -270,18 +286,21 @@ static void updateViaExec(void) { } static void SystemdMeter_updateValues(Meter* this) { - free(systemState); - systemState = NULL; - nFailedUnits = nInstalledJobs = nNames = nJobs = INVALID_VALUE; + bool user = String_eq(Meter_name(this), "SystemdUser"); + SystemdMeterContext_t* ctx = user ? &ctx_user : &ctx_system; + + free(ctx->systemState); + ctx->systemState = NULL; + ctx->nFailedUnits = ctx->nInstalledJobs = ctx->nNames = ctx->nJobs = INVALID_VALUE; #if !defined(BUILD_STATIC) || defined(HAVE_LIBSYSTEMD) - if (updateViaLib() < 0) - updateViaExec(); + if (updateViaLib(user) < 0) + updateViaExec(user); #else - updateViaExec(); + updateViaExec(user); #endif /* !BUILD_STATIC || HAVE_LIBSYSTEMD */ - xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%s", systemState ? systemState : "???"); + xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%s", ctx->systemState ? ctx->systemState : "???"); } static int zeroDigitColor(unsigned int value) { @@ -307,64 +326,72 @@ static int valueDigitColor(unsigned int value) { } -static void SystemdMeter_display(ATTR_UNUSED const Object* cast, RichString* out) { +static void _SystemdMeter_display(ATTR_UNUSED const Object* cast, RichString* out, SystemdMeterContext_t* ctx) { char buffer[16]; int len; int color = METER_VALUE_ERROR; - if (systemState) { - color = String_eq(systemState, "running") ? METER_VALUE_OK : - String_eq(systemState, "degraded") ? METER_VALUE_ERROR : METER_VALUE_WARN; + if (ctx->systemState) { + color = String_eq(ctx->systemState, "running") ? METER_VALUE_OK : + String_eq(ctx->systemState, "degraded") ? METER_VALUE_ERROR : METER_VALUE_WARN; } - RichString_writeAscii(out, CRT_colors[color], systemState ? systemState : "N/A"); + RichString_writeAscii(out, CRT_colors[color], ctx->systemState ? ctx->systemState : "N/A"); RichString_appendAscii(out, CRT_colors[METER_TEXT], " ("); - if (nFailedUnits == INVALID_VALUE) { + if (ctx->nFailedUnits == INVALID_VALUE) { buffer[0] = '?'; buffer[1] = '\0'; len = 1; } else { - len = xSnprintf(buffer, sizeof(buffer), "%u", nFailedUnits); + len = xSnprintf(buffer, sizeof(buffer), "%u", ctx->nFailedUnits); } - RichString_appendnAscii(out, zeroDigitColor(nFailedUnits), buffer, len); + RichString_appendnAscii(out, zeroDigitColor(ctx->nFailedUnits), buffer, len); RichString_appendAscii(out, CRT_colors[METER_TEXT], "/"); - if (nNames == INVALID_VALUE) { + if (ctx->nNames == INVALID_VALUE) { buffer[0] = '?'; buffer[1] = '\0'; len = 1; } else { - len = xSnprintf(buffer, sizeof(buffer), "%u", nNames); + len = xSnprintf(buffer, sizeof(buffer), "%u", ctx->nNames); } - RichString_appendnAscii(out, valueDigitColor(nNames), buffer, len); + RichString_appendnAscii(out, valueDigitColor(ctx->nNames), buffer, len); RichString_appendAscii(out, CRT_colors[METER_TEXT], " failed) ("); - if (nJobs == INVALID_VALUE) { + if (ctx->nJobs == INVALID_VALUE) { buffer[0] = '?'; buffer[1] = '\0'; len = 1; } else { - len = xSnprintf(buffer, sizeof(buffer), "%u", nJobs); + len = xSnprintf(buffer, sizeof(buffer), "%u", ctx->nJobs); } - RichString_appendnAscii(out, zeroDigitColor(nJobs), buffer, len); + RichString_appendnAscii(out, zeroDigitColor(ctx->nJobs), buffer, len); RichString_appendAscii(out, CRT_colors[METER_TEXT], "/"); - if (nInstalledJobs == INVALID_VALUE) { + if (ctx->nInstalledJobs == INVALID_VALUE) { buffer[0] = '?'; buffer[1] = '\0'; len = 1; } else { - len = xSnprintf(buffer, sizeof(buffer), "%u", nInstalledJobs); + len = xSnprintf(buffer, sizeof(buffer), "%u", ctx->nInstalledJobs); } - RichString_appendnAscii(out, valueDigitColor(nInstalledJobs), buffer, len); + RichString_appendnAscii(out, valueDigitColor(ctx->nInstalledJobs), buffer, len); RichString_appendAscii(out, CRT_colors[METER_TEXT], " jobs)"); } +static void SystemdMeter_display(ATTR_UNUSED const Object* cast, RichString* out) { + _SystemdMeter_display(cast, out, &ctx_system); +} + +static void SystemdUserMeter_display(ATTR_UNUSED const Object* cast, RichString* out) { + _SystemdMeter_display(cast, out, &ctx_user); +} + static const int SystemdMeter_attributes[] = { METER_VALUE }; @@ -386,3 +413,21 @@ const MeterClass SystemdMeter_class = { .description = "Systemd system state and unit overview", .caption = "Systemd: ", }; + +const MeterClass SystemdUserMeter_class = { + .super = { + .extends = Class(Meter), + .delete = Meter_delete, + .display = SystemdUserMeter_display + }, + .updateValues = SystemdMeter_updateValues, + .done = SystemdMeter_done, + .defaultMode = TEXT_METERMODE, + .maxItems = 0, + .total = 100.0, + .attributes = SystemdMeter_attributes, + .name = "SystemdUser", + .uiName = "Systemd user state", + .description = "Systemd user state and unit overview", + .caption = "Systemd User: ", +}; diff --git a/linux/SystemdMeter.h b/linux/SystemdMeter.h index a05e0872d..50a793b32 100644 --- a/linux/SystemdMeter.h +++ b/linux/SystemdMeter.h @@ -13,4 +13,6 @@ in the source distribution for its full text. extern const MeterClass SystemdMeter_class; +extern const MeterClass SystemdUserMeter_class; + #endif /* HEADER_SystemdMeter */ From da494896914a327476ab7e0298619d742a6205d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= Date: Tue, 10 Jan 2023 19:40:04 +0100 Subject: [PATCH 039/106] Add support for scheduling policies Add a process column for scheduling policy to show the current scheduling policy of the process. Add a the ability to change the scheduling policy of a process via the key 'Y'. Currently implemented on Linux and FreeBSD only but should be portable, since sched_getscheduler(2) is part of POSIX.1-2001. Closes: #1161 --- Action.c | 52 ++++++++++++ Makefile.am | 2 + Process.c | 12 +++ Process.h | 5 ++ Scheduling.c | 154 +++++++++++++++++++++++++++++++++++ Scheduling.h | 49 +++++++++++ configure.ac | 2 + freebsd/FreeBSDProcess.c | 4 + freebsd/FreeBSDProcessList.c | 6 ++ linux/LinuxProcess.c | 4 + linux/LinuxProcessList.c | 7 ++ 11 files changed, 297 insertions(+) create mode 100644 Scheduling.c create mode 100644 Scheduling.h diff --git a/Action.c b/Action.c index 81432f5de..5d33f6624 100644 --- a/Action.c +++ b/Action.c @@ -29,6 +29,7 @@ in the source distribution for its full text. #include "Process.h" #include "ProcessLocksScreen.h" #include "ProvideCurses.h" +#include "Scheduling.h" #include "ScreenManager.h" #include "SignalsPanel.h" #include "TraceScreen.h" @@ -407,6 +408,51 @@ static Htop_Reaction actionSetAffinity(State* st) { } +#ifdef SCHEDULER_SUPPORT +static Htop_Reaction actionSetSchedPolicy(State* st) { + if (Settings_isReadonly()) + return HTOP_KEEP_FOLLOWING; + + static int preSelectedPolicy = SCHEDULINGPANEL_INITSELECTEDPOLICY; + static int preSelectedPriority = SCHEDULINGPANEL_INITSELECTEDPRIORITY; + + Panel* schedPanel = Scheduling_newPolicyPanel(preSelectedPolicy); + + const ListItem* policy; + for(;;) { + policy = (const ListItem*) Action_pickFromVector(st, schedPanel, 18, true); + + if (!policy || policy->key != -1) + break; + + Scheduling_togglePolicyPanelResetOnFork(schedPanel); + } + + if (policy) { + preSelectedPolicy = policy->key; + + Panel* prioPanel = Scheduling_newPriorityPanel(policy->key, preSelectedPriority); + if (prioPanel) { + const ListItem* prio = (const ListItem*) Action_pickFromVector(st, prioPanel, 14, true); + if (prio) + preSelectedPriority = prio->key; + + Panel_delete((Object*) prioPanel); + } + + SchedulingArg v = { .policy = preSelectedPolicy, .priority = preSelectedPriority }; + + bool ok = MainPanel_foreachProcess(st->mainPanel, Scheduling_setPolicy, (Arg) { .v = &v }, NULL); + if (!ok) + beep(); + } + + Panel_delete((Object*)schedPanel); + + return HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_KEEP_FOLLOWING; +} +#endif /* SCHEDULER_SUPPORT */ + static Htop_Reaction actionKill(State* st) { if (Settings_isReadonly()) return HTOP_OK; @@ -571,6 +617,9 @@ static const struct { { .key = " x: ", .roInactive = false, .info = "list file locks of process" }, { .key = " s: ", .roInactive = true, .info = "trace syscalls with strace" }, { .key = " w: ", .roInactive = false, .info = "wrap process command in multiple lines" }, +#ifdef SCHEDULER_SUPPORT + { .key = " Y: ", .roInactive = true, .info = "set scheduling policy" }, +#endif { .key = " F2 C S: ", .roInactive = false, .info = "setup" }, { .key = " F1 h ?: ", .roInactive = false, .info = "show this help screen" }, { .key = " F10 q: ", .roInactive = false, .info = "quit" }, @@ -779,6 +828,9 @@ void Action_setBindings(Htop_Action* keys) { keys['S'] = actionSetup; keys['T'] = actionSortByTime; keys['U'] = actionUntagAll; +#ifdef SCHEDULER_SUPPORT + keys['Y'] = actionSetSchedPolicy; +#endif keys['Z'] = actionTogglePauseProcessUpdate; keys['['] = actionLowerPriority; keys['\014'] = actionRedraw; // Ctrl+L diff --git a/Makefile.am b/Makefile.am index 8af1864ac..1c685e4d3 100644 --- a/Makefile.am +++ b/Makefile.am @@ -73,6 +73,7 @@ myhtopsources = \ ProcessList.c \ ProcessLocksScreen.c \ RichString.c \ + Scheduling.c \ ScreenManager.c \ ScreensPanel.c \ Settings.c \ @@ -135,6 +136,7 @@ myhtopheaders = \ ProcessLocksScreen.h \ ProvideCurses.h \ RichString.h \ + Scheduling.h \ ScreenManager.h \ ScreensPanel.h \ Settings.h \ diff --git a/Process.c b/Process.c index fcaa3d540..614369b40 100644 --- a/Process.c +++ b/Process.c @@ -28,6 +28,7 @@ in the source distribution for its full text. #include "ProcessList.h" #include "DynamicColumn.h" #include "RichString.h" +#include "Scheduling.h" #include "Settings.h" #include "XUtils.h" @@ -960,6 +961,15 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field xSnprintf(buffer, n, "%3ld ", this->priority); break; case PROCESSOR: xSnprintf(buffer, n, "%3d ", Settings_cpuId(this->settings, this->processor)); break; + case SCHEDULERPOLICY: { + const char* schedPolStr = "N/A"; +#ifdef SCHEDULER_SUPPORT + if (this->scheduling_policy >= 0) + schedPolStr = Scheduling_formatPolicy(this->scheduling_policy); +#endif + xSnprintf(buffer, n, "%-5s ", schedPolStr); + break; + } case SESSION: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->session); break; case STARTTIME: xSnprintf(buffer, n, "%s", this->starttime_show); break; case STATE: @@ -1203,6 +1213,8 @@ int Process_compareByKey_Base(const Process* p1, const Process* p2, ProcessField return SPACESHIP_NUMBER(p1->priority, p2->priority); case PROCESSOR: return SPACESHIP_NUMBER(p1->processor, p2->processor); + case SCHEDULERPOLICY: + return SPACESHIP_NUMBER(p1->scheduling_policy, p2->scheduling_policy); case SESSION: return SPACESHIP_NUMBER(p1->session, p2->session); case STARTTIME: diff --git a/Process.h b/Process.h index eb79470d3..0fdc392be 100644 --- a/Process.h +++ b/Process.h @@ -19,6 +19,7 @@ in the source distribution for its full text. #define PROCESS_FLAG_IO 0x00000001 #define PROCESS_FLAG_CWD 0x00000002 +#define PROCESS_FLAG_SCHEDPOL 0x00000004 #define DEFAULT_HIGHLIGHT_SECS 5 @@ -49,6 +50,7 @@ typedef enum ProcessField_ { TGID = 52, PERCENT_NORM_CPU = 53, ELAPSED = 54, + SCHEDULERPOLICY = 55, PROC_COMM = 124, PROC_EXE = 125, CWD = 126, @@ -221,6 +223,9 @@ typedef struct Process_ { /* Process state enum field (platform dependent) */ ProcessState state; + /* Current scheduling policy */ + int scheduling_policy; + /* Whether the process was updated during the current scan */ bool updated; diff --git a/Scheduling.c b/Scheduling.c new file mode 100644 index 000000000..5ca49ae2d --- /dev/null +++ b/Scheduling.c @@ -0,0 +1,154 @@ +/* +htop - Scheduling.c +(C) 2023 htop dev team +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "Scheduling.h" +#include "EnvScreen.h" + +#ifdef SCHEDULER_SUPPORT + +#include + +#include "FunctionBar.h" +#include "ListItem.h" +#include "Macros.h" +#include "Object.h" +#include "Panel.h" +#include "XUtils.h" + + +static const SchedulingPolicy policies[] = { + [SCHED_OTHER] = { "Other", SCHED_OTHER, false }, +#ifdef SCHED_BATCH + [SCHED_BATCH] = { "Batch", SCHED_BATCH, false }, +#endif +#ifdef SCHED_IDLE + [SCHED_IDLE] = { "Idle", SCHED_IDLE, false }, +#endif + [SCHED_FIFO] = { "FiFo", SCHED_FIFO, true }, + [SCHED_RR] = { "RoundRobin", SCHED_RR, true }, +}; + +#ifdef SCHED_RESET_ON_FORK +static bool reset_on_fork = false; +#endif + + +Panel* Scheduling_newPolicyPanel(int preSelectedPolicy) { + Panel* this = Panel_new(0, 0, 0, 0, Class(ListItem), true, FunctionBar_newEnterEsc("Select ", "Cancel ")); + Panel_setHeader(this, "New policy:"); + +#ifdef SCHED_RESET_ON_FORK + Panel_add(this, (Object*) ListItem_new(reset_on_fork ? "Reset on fork: on" : "Reset on fork: off", -1)); +#endif + + for (unsigned i = 0; i < ARRAYSIZE(policies); i++) { + if (!policies[i].name) + continue; + + Panel_add(this, (Object*) ListItem_new(policies[i].name, policies[i].id)); + if (policies[i].id == preSelectedPolicy) + Panel_setSelected(this, i); + } + + return this; +} + +void Scheduling_togglePolicyPanelResetOnFork(Panel* schedPanel) { +#ifdef SCHED_RESET_ON_FORK + reset_on_fork = !reset_on_fork; + + ListItem* item = (ListItem*) Panel_get(schedPanel, 0); + + free_and_xStrdup(&item->value, reset_on_fork ? "Reset on fork: on" : "Reset on fork: off"); +#else + (void)schedPanel; +#endif +} + +Panel* Scheduling_newPriorityPanel(int policy, int preSelectedPriority) { + if (policy < 0 || (unsigned)policy >= ARRAYSIZE(policies) || policies[policy].name == NULL) + return NULL; + + if (!policies[policy].prioritySupport) + return NULL; + + int min = sched_get_priority_min(policy); + if (min < 0) + return NULL; + int max = sched_get_priority_max(policy); + if (max < 0 ) + return NULL; + + Panel* this = Panel_new(0, 0, 0, 0, Class(ListItem), true, FunctionBar_newEnterEsc("Select ", "Cancel ")); + Panel_setHeader(this, "Priority:"); + + for (int i = min; i <= max; i++) { + char buf[16]; + xSnprintf(buf, sizeof(buf), "%d", i); + Panel_add(this, (Object*) ListItem_new(buf, i)); + if (i == preSelectedPriority) + Panel_setSelected(this, i); + } + + return this; +} + +bool Scheduling_setPolicy(Process* proc, Arg arg) { + const SchedulingArg* sarg = arg.v; + int policy = sarg->policy; + + assert(policy >= 0); + assert((unsigned)policy < ARRAYSIZE(policies)); + assert(policies[policy].name); + + const struct sched_param param = { .sched_priority = policies[policy].prioritySupport ? sarg->priority : 0 }; + + #ifdef SCHED_RESET_ON_FORK + if (reset_on_fork) + policy &= SCHED_RESET_ON_FORK; + #endif + + int r = sched_setscheduler(proc->pid, policy, ¶m); + + /* POSIX says on success the previous scheduling policy should be returned, + * but Linux always returns 0. */ + return r != -1; +} + +const char* Scheduling_formatPolicy(int policy) { +#ifdef SCHED_RESET_ON_FORK + policy = policy & ~SCHED_RESET_ON_FORK; +#endif + + switch (policy) { + case SCHED_OTHER: + return "OTHER"; + case SCHED_FIFO: + return "FIFO"; + case SCHED_RR: + return "RR"; +#ifdef SCHED_BATCH + case SCHED_BATCH: + return "BATCH"; +#endif +#ifdef SCHED_IDLE + case SCHED_IDLE: + return "IDLE"; +#endif +#ifdef SCHED_DEADLINE + case SCHED_DEADLINE: + return "EDF"; +#endif + default: + return "???"; + } +} + +void Scheduling_readProcessPolicy(Process* proc) { + proc->scheduling_policy = sched_getscheduler(proc->pid); +} +#endif /* SCHEDULER_SUPPORT */ diff --git a/Scheduling.h b/Scheduling.h new file mode 100644 index 000000000..d91855ae4 --- /dev/null +++ b/Scheduling.h @@ -0,0 +1,49 @@ +#ifndef HEADER_Scheduling +#define HEADER_Scheduling +/* +htop - Scheduling.h +(C) 2023 htop dev team +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "config.h" // IWYU pragma: keep + +#include +#include + +#include "Panel.h" + + +#if defined(HAVE_SCHED_SETSCHEDULER) && defined(HAVE_SCHED_GETSCHEDULER) +#define SCHEDULER_SUPPORT + +typedef struct { + const char* name; + int id; + bool prioritySupport; +} SchedulingPolicy; + +#define SCHEDULINGPANEL_INITSELECTEDPOLICY SCHED_OTHER +#define SCHEDULINGPANEL_INITSELECTEDPRIORITY 50 + +Panel* Scheduling_newPolicyPanel(int preSelectedPolicy); +void Scheduling_togglePolicyPanelResetOnFork(Panel* schedPanel); + +Panel* Scheduling_newPriorityPanel(int policy, int preSelectedPriority); + + +typedef struct { + int policy; + int priority; +} SchedulingArg; + +bool Scheduling_setPolicy(Process* proc, Arg arg); + +const char* Scheduling_formatPolicy(int policy); + +void Scheduling_readProcessPolicy(Process* proc); + +#endif + +#endif /* HEADER_Scheduling */ diff --git a/configure.ac b/configure.ac index de0718e14..73a8e91c8 100644 --- a/configure.ac +++ b/configure.ac @@ -260,6 +260,8 @@ AC_CHECK_FUNCS([ \ memfd_create\ openat \ readlinkat \ + sched_getscheduler \ + sched_setscheduler \ ]) if test "$my_htop_platform" = darwin; then diff --git a/freebsd/FreeBSDProcess.c b/freebsd/FreeBSDProcess.c index 4eccfe7fd..90bc1f2c3 100644 --- a/freebsd/FreeBSDProcess.c +++ b/freebsd/FreeBSDProcess.c @@ -13,6 +13,7 @@ in the source distribution for its full text. #include "Macros.h" #include "Process.h" #include "RichString.h" +#include "Scheduling.h" #include "XUtils.h" @@ -47,6 +48,9 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = { [PROC_COMM] = { .name = "COMM", .title = "COMM ", .description = "comm string of the process", .flags = 0, }, [PROC_EXE] = { .name = "EXE", .title = "EXE ", .description = "Basename of exe of the process", .flags = 0, }, [CWD] = { .name = "CWD", .title = "CWD ", .description = "The current working directory of the process", .flags = PROCESS_FLAG_CWD, }, +#ifdef SCHEDULER_SUPPORT + [SCHEDULERPOLICY] = { .name = "SCHEDULERPOLICY", .title = "SCHED ", .description = "Current scheduling policy of the process", .flags = PROCESS_FLAG_SCHEDPOL, }, +#endif [JID] = { .name = "JID", .title = "JID", .description = "Jail prison ID", .flags = 0, .pidColumn = true, }, [JAIL] = { .name = "JAIL", .title = "JAIL ", .description = "Jail prison name", .flags = 0, }, [EMULATION] = { .name = "EMULATION", .title = "EMULATION ", .description = "System call emulation environment (ABI)", .flags = 0, }, diff --git a/freebsd/FreeBSDProcessList.c b/freebsd/FreeBSDProcessList.c index 9ee5ea402..98447df9a 100644 --- a/freebsd/FreeBSDProcessList.c +++ b/freebsd/FreeBSDProcessList.c @@ -34,6 +34,7 @@ in the source distribution for its full text. #include "Object.h" #include "Process.h" #include "ProcessList.h" +#include "Scheduling.h" #include "Settings.h" #include "XUtils.h" #include "generic/openzfs_sysctl.h" @@ -606,6 +607,11 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { if (Process_isKernelThread(proc)) super->kernelThreads++; +#ifdef SCHEDULER_SUPPORT + if (settings->ss->flags & PROCESS_FLAG_SCHEDPOL) + Scheduling_readProcessPolicy(proc); +#endif + proc->show = ! ((hideKernelThreads && Process_isKernelThread(proc)) || (hideUserlandThreads && Process_isUserlandThread(proc))); super->totalTasks++; diff --git a/linux/LinuxProcess.c b/linux/LinuxProcess.c index 381b7cf5f..8f9c34620 100644 --- a/linux/LinuxProcess.c +++ b/linux/LinuxProcess.c @@ -19,6 +19,7 @@ in the source distribution for its full text. #include "Process.h" #include "ProvideCurses.h" #include "RichString.h" +#include "Scheduling.h" #include "XUtils.h" #include "linux/IOPriority.h" @@ -100,6 +101,9 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = { [CWD] = { .name = "CWD", .title = "CWD ", .description = "The current working directory of the process", .flags = PROCESS_FLAG_CWD, }, [AUTOGROUP_ID] = { .name = "AUTOGROUP_ID", .title = "AGRP", .description = "The autogroup identifier of the process", .flags = PROCESS_FLAG_LINUX_AUTOGROUP, }, [AUTOGROUP_NICE] = { .name = "AUTOGROUP_NICE", .title = " ANI", .description = "Nice value (the higher the value, the more other processes take priority) associated with the process autogroup", .flags = PROCESS_FLAG_LINUX_AUTOGROUP, }, +#ifdef SCHEDULER_SUPPORT + [SCHEDULERPOLICY] = { .name = "SCHEDULERPOLICY", .title = "SCHED ", .description = "Current scheduling policy of the process", .flags = PROCESS_FLAG_SCHEDPOL, }, +#endif }; Process* LinuxProcess_new(const Settings* settings) { diff --git a/linux/LinuxProcessList.c b/linux/LinuxProcessList.c index eca9459b5..8490d82c1 100644 --- a/linux/LinuxProcessList.c +++ b/linux/LinuxProcessList.c @@ -44,6 +44,7 @@ in the source distribution for its full text. #include "Macros.h" #include "Object.h" #include "Process.h" +#include "Scheduling.h" #include "Settings.h" #include "XUtils.h" #include "linux/CGroupUtils.h" @@ -1721,6 +1722,12 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ LinuxProcessList_readAutogroup(lp, procFd); } + #ifdef SCHEDULER_SUPPORT + if (ss->flags & PROCESS_FLAG_SCHEDPOL) { + Scheduling_readProcessPolicy(proc); + } + #endif + if (!proc->cmdline && statCommand[0] && (proc->state == ZOMBIE || Process_isKernelThread(proc) || settings->showThreadNames)) { Process_updateCmdline(proc, statCommand, 0, strlen(statCommand)); From 436279a01c8e49265c504eb4e6bc5b97fe8290d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= Date: Sat, 29 Oct 2022 19:18:52 +0200 Subject: [PATCH 040/106] Linux: move helper function to top of file Make them reusable for function defined earlier in the file. --- linux/LinuxProcessList.c | 84 ++++++++++++++++++++-------------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/linux/LinuxProcessList.c b/linux/LinuxProcessList.c index 8490d82c1..27792bf8a 100644 --- a/linux/LinuxProcessList.c +++ b/linux/LinuxProcessList.c @@ -88,6 +88,48 @@ static FILE* fopenat(openat_arg_t openatArg, const char* pathname, const char* m return stream; } +static inline uint64_t fast_strtoull_dec(char** str, int maxlen) { + register uint64_t result = 0; + + if (!maxlen) + --maxlen; + + while (maxlen-- && **str >= '0' && **str <= '9') { + result *= 10; + result += **str - '0'; + (*str)++; + } + + return result; +} + +static inline uint64_t fast_strtoull_hex(char** str, int maxlen) { + register uint64_t result = 0; + register int nibble, letter; + const long valid_mask = 0x03FF007E; + + if (!maxlen) + --maxlen; + + while (maxlen--) { + nibble = (unsigned char)**str; + if (!(valid_mask & (1 << (nibble & 0x1F)))) + break; + if ((nibble < '0') || (nibble & ~0x20) > 'F') + break; + letter = (nibble & 0x40) ? 'A' - '9' - 1 : 0; + nibble &=~0x20; // to upper + nibble ^= 0x10; // switch letters and digits + nibble -= letter; + nibble &= 0x0f; + result <<= 4; + result += (uint64_t)nibble; + (*str)++; + } + + return result; +} + static int sortTtyDrivers(const void* va, const void* vb) { const TtyDriver* a = (const TtyDriver*) va; const TtyDriver* b = (const TtyDriver*) vb; @@ -574,48 +616,6 @@ typedef struct LibraryData_ { bool exec; } LibraryData; -static inline uint64_t fast_strtoull_dec(char** str, int maxlen) { - register uint64_t result = 0; - - if (!maxlen) - --maxlen; - - while (maxlen-- && **str >= '0' && **str <= '9') { - result *= 10; - result += **str - '0'; - (*str)++; - } - - return result; -} - -static inline uint64_t fast_strtoull_hex(char** str, int maxlen) { - register uint64_t result = 0; - register int nibble, letter; - const long valid_mask = 0x03FF007E; - - if (!maxlen) - --maxlen; - - while (maxlen--) { - nibble = (unsigned char)**str; - if (!(valid_mask & (1 << (nibble & 0x1F)))) - break; - if ((nibble < '0') || (nibble & ~0x20) > 'F') - break; - letter = (nibble & 0x40) ? 'A' - '9' - 1 : 0; - nibble &=~0x20; // to upper - nibble ^= 0x10; // switch letters and digits - nibble -= letter; - nibble &= 0x0f; - result <<= 4; - result += (uint64_t)nibble; - (*str)++; - } - - return result; -} - static void LinuxProcessList_calcLibSize_helper(ATTR_UNUSED ht_key_t key, void* value, void* data) { if (!data) return; From 8ea144df7494bad0c46d3bf4f16c9a6556c500d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= Date: Sat, 29 Oct 2022 19:20:54 +0200 Subject: [PATCH 041/106] Linux: Refactor /proc//status parsing Merge parsing /proc//status for context switches, vserver ID and container detection into one function. (OpenVZ still does a separate cycle but is left as an exercise for someone actually using it.) --- linux/LinuxProcessList.c | 216 +++++++++++++++++---------------------- 1 file changed, 92 insertions(+), 124 deletions(-) diff --git a/linux/LinuxProcessList.c b/linux/LinuxProcessList.c index 27792bf8a..0576dd67e 100644 --- a/linux/LinuxProcessList.c +++ b/linux/LinuxProcessList.c @@ -534,6 +534,80 @@ static bool LinuxProcessList_readStatFile(Process* process, openat_arg_t procFd, return true; } +static bool LinuxProcessList_readStatusFile(Process* process, openat_arg_t procFd) { + LinuxProcess* lp = (LinuxProcess*) process; + + unsigned long ctxt = 0; +#ifdef HAVE_VSERVER + lp->vxid = 0; +#endif + + FILE* statusfile = fopenat(procFd, "status", "r"); + if (!statusfile) + return false; + + char buffer[PROC_LINE_LENGTH + 1]; + + while (fgets(buffer, sizeof(buffer), statusfile)) { + + if (String_startsWith(buffer, "NSpid:")) { + const char* ptr = buffer; + int pid_ns_count = 0; + while (*ptr && *ptr != '\n' && !isdigit((unsigned char)*ptr)) + ++ptr; + + while (*ptr && *ptr != '\n') { + if (isdigit(*ptr)) + pid_ns_count++; + while (isdigit((unsigned char)*ptr)) + ++ptr; + while (*ptr && *ptr != '\n' && !isdigit((unsigned char)*ptr)) + ++ptr; + } + + if (pid_ns_count > 1) + process->isRunningInContainer = true; + + } else if (String_startsWith(buffer, "voluntary_ctxt_switches:")) { + unsigned long vctxt; + int ok = sscanf(buffer, "voluntary_ctxt_switches:\t%lu", &vctxt); + if (ok >= 1) { + ctxt += vctxt; + } + + } else if (String_startsWith(buffer, "nonvoluntary_ctxt_switches:")) { + unsigned long nvctxt; + int ok = sscanf(buffer, "nonvoluntary_ctxt_switches:\t%lu", &nvctxt); + if (ok >= 1) { + ctxt += nvctxt; + } + +#ifdef HAVE_VSERVER + } else if (String_startsWith(buffer, "VxID:")) { + int vxid; + int ok = sscanf(buffer, "VxID:\t%32d", &vxid); + if (ok >= 1) { + lp->vxid = vxid; + } +#ifdef HAVE_ANCIENT_VSERVER + } else if (String_startsWith(buffer, "s_context:")) { + int vxid; + int ok = sscanf(buffer, "s_context:\t%32d", &vxid); + if (ok >= 1) { + lp->vxid = vxid; + } +#endif /* HAVE_ANCIENT_VSERVER */ +#endif /* HAVE_VSERVER */ + } + } + + fclose(statusfile); + + lp->ctxt_diff = (ctxt > lp->ctxt_total) ? (ctxt - lp->ctxt_total) : 0; + lp->ctxt_total = ctxt; + + return true; +} static bool LinuxProcessList_updateUser(ProcessList* processList, Process* process, openat_arg_t procFd) { struct stat sstat; @@ -765,43 +839,6 @@ static bool LinuxProcessList_readStatmFile(LinuxProcess* process, openat_arg_t p return r == 7; } -static bool LinuxProcessList_checkPidNamespace(Process* process, openat_arg_t procFd) { - FILE* statusfile = fopenat(procFd, "status", "r"); - if (!statusfile) - return false; - - while (true) { - char buffer[PROC_LINE_LENGTH + 1]; - if (fgets(buffer, sizeof(buffer), statusfile) == NULL) - break; - - if (!String_startsWith(buffer, "NSpid:")) - continue; - - char* ptr = buffer; - int pid_ns_count = 0; - while (*ptr && *ptr != '\n' && !isdigit(*ptr)) - ++ptr; - - while (*ptr && *ptr != '\n') { - if (isdigit(*ptr)) - pid_ns_count++; - while (isdigit(*ptr)) - ++ptr; - while (*ptr && *ptr != '\n' && !isdigit(*ptr)) - ++ptr; - } - - if (pid_ns_count > 1) - process->isRunningInContainer = true; - - break; - } - - fclose(statusfile); - return true; -} - static bool LinuxProcessList_readSmapsFile(LinuxProcess* process, openat_arg_t procFd, bool haveSmapsRollup) { //http://elixir.free-electrons.com/linux/v4.10/source/fs/proc/task_mmu.c#L719 //kernel will return data in chunks of size PAGE_SIZE or less. @@ -930,13 +967,6 @@ static void LinuxProcessList_readOpenVZData(LinuxProcess* process, openat_arg_t #endif -static bool isContainerOrVMSlice(char* cgroup) { - if (String_startsWith(cgroup, "/user") || String_startsWith(cgroup, "/system")) - return false; - - return true; -} - static void LinuxProcessList_readCGroupFile(LinuxProcess* process, openat_arg_t procFd) { FILE* file = fopenat(procFd, "cgroup", "r"); if (!file) { @@ -1009,38 +1039,6 @@ static void LinuxProcessList_readCGroupFile(LinuxProcess* process, openat_arg_t } } -#ifdef HAVE_VSERVER - -static void LinuxProcessList_readVServerData(LinuxProcess* process, openat_arg_t procFd) { - FILE* file = fopenat(procFd, "status", "r"); - if (!file) - return; - - char buffer[PROC_LINE_LENGTH + 1]; - process->vxid = 0; - while (fgets(buffer, PROC_LINE_LENGTH, file)) { - if (String_startsWith(buffer, "VxID:")) { - int vxid; - int ok = sscanf(buffer, "VxID:\t%32d", &vxid); - if (ok >= 1) { - process->vxid = vxid; - } - } - #if defined HAVE_ANCIENT_VSERVER - else if (String_startsWith(buffer, "s_context:")) { - int vxid; - int ok = sscanf(buffer, "s_context:\t%32d", &vxid); - if (ok >= 1) { - process->vxid = vxid; - } - } - #endif - } - fclose(file); -} - -#endif - static void LinuxProcessList_readOomData(LinuxProcess* process, openat_arg_t procFd) { FILE* file = fopenat(procFd, "oom_score", "r"); if (!file) @@ -1074,33 +1072,6 @@ static void LinuxProcessList_readAutogroup(LinuxProcess* process, openat_arg_t p } } -static void LinuxProcessList_readCtxtData(LinuxProcess* process, openat_arg_t procFd) { - FILE* file = fopenat(procFd, "status", "r"); - if (!file) - return; - - char buffer[PROC_LINE_LENGTH + 1]; - unsigned long ctxt = 0; - while (fgets(buffer, PROC_LINE_LENGTH, file)) { - if (String_startsWith(buffer, "voluntary_ctxt_switches:")) { - unsigned long vctxt; - int ok = sscanf(buffer, "voluntary_ctxt_switches:\t%lu", &vctxt); - if (ok >= 1) { - ctxt += vctxt; - } - } else if (String_startsWith(buffer, "nonvoluntary_ctxt_switches:")) { - unsigned long nvctxt; - int ok = sscanf(buffer, "nonvoluntary_ctxt_switches:\t%lu", &nvctxt); - if (ok >= 1) { - ctxt += nvctxt; - } - } - } - fclose(file); - process->ctxt_diff = (ctxt > process->ctxt_total) ? (ctxt - process->ctxt_total) : 0; - process->ctxt_total = ctxt; -} - static void LinuxProcessList_readSecattrData(LinuxProcess* process, openat_arg_t procFd) { FILE* file = fopenat(procFd, "attr/current", "r"); if (!file) { @@ -1551,15 +1522,6 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ LinuxProcessList_recurseProcTree(this, procFd, "task", proc, period); - if (ss->flags & PROCESS_FLAG_LINUX_CGROUP || hideRunningInContainer) { - LinuxProcessList_readCGroupFile(lp, procFd); - if (hideRunningInContainer && lp->cgroup && isContainerOrVMSlice(lp->cgroup)) { - if (!LinuxProcessList_checkPidNamespace(proc, procFd)) { - goto errorReadingProcess; - } - } - } - /* * These conditions will not trigger on first occurrence, cause we need to * add the process to the ProcessList and do all one time scans @@ -1663,6 +1625,9 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ if (! LinuxProcessList_updateUser(pl, proc, procFd)) goto errorReadingProcess; + if (!LinuxProcessList_readStatusFile(proc, procFd)) + goto errorReadingProcess; + if (!preExisting) { #ifdef HAVE_OPENVZ @@ -1671,12 +1636,6 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ } #endif - #ifdef HAVE_VSERVER - if (ss->flags & PROCESS_FLAG_LINUX_VSERVER) { - LinuxProcessList_readVServerData(lp, procFd); - } - #endif - if (proc->isKernelThread) { Process_updateCmdline(proc, NULL, 0, 0); } else if (!LinuxProcessList_readCmdlineFile(proc, procFd)) { @@ -1696,6 +1655,9 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ } } + if (ss->flags & PROCESS_FLAG_LINUX_CGROUP) + LinuxProcessList_readCGroupFile(lp, procFd); + #ifdef HAVE_DELAYACCT if (ss->flags & PROCESS_FLAG_LINUX_DELAYACCT) { LinuxProcessList_readDelayAcctData(this, lp); @@ -1706,10 +1668,6 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ LinuxProcessList_readOomData(lp, procFd); } - if (ss->flags & PROCESS_FLAG_LINUX_CTXT) { - LinuxProcessList_readCtxtData(lp, procFd); - } - if (ss->flags & PROCESS_FLAG_LINUX_SECATTR) { LinuxProcessList_readSecattrData(lp, procFd); } @@ -1733,6 +1691,18 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ Process_updateCmdline(proc, statCommand, 0, strlen(statCommand)); } + /* + * Final section after all data has been gathered + */ + + proc->updated = true; + Compat_openatArgClose(procFd); + + if (hideRunningInContainer && proc->isRunningInContainer) { + proc->show = false; + continue; + } + if (Process_isKernelThread(proc)) { pl->kernelThreads++; } else if (Process_isUserlandThread(proc)) { @@ -1744,8 +1714,6 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ pl->totalTasks++; /* runningTasks is set in LinuxProcessList_scanCPUTime() from /proc/stat */ - proc->updated = true; - Compat_openatArgClose(procFd); continue; // Exception handler. From e3481a9846ef01bb27c169b71eafeee704e10c68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= Date: Sat, 29 Oct 2022 19:21:12 +0200 Subject: [PATCH 042/106] Linux: highlight username if process has elevated privileges Highlight processes started from binaries with file capabilities set, like kwin_wayland, or retaining Linux capabilities, via the ambient set, after switching from the root user, e.g. rtkit. --- CRT.c | 6 ++++++ CRT.h | 1 + Process.c | 4 +++- Process.h | 7 +++++++ linux/LinuxProcessList.c | 8 ++++++++ 5 files changed, 25 insertions(+), 1 deletion(-) diff --git a/CRT.c b/CRT.c index 64c259fcf..e35608b6c 100644 --- a/CRT.c +++ b/CRT.c @@ -157,6 +157,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Green, Black), [PROCESS_COMM] = ColorPair(Magenta, Black), [PROCESS_THREAD_COMM] = A_BOLD | ColorPair(Blue, Black), + [PROCESS_PRIV] = ColorPair(Magenta, Black), [BAR_BORDER] = A_BOLD, [BAR_SHADOW] = A_BOLD | ColorPairGrayBlack, [SWAP] = ColorPair(Red, Black), @@ -264,6 +265,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [PROCESS_THREAD_BASENAME] = A_REVERSE, [PROCESS_COMM] = A_BOLD, [PROCESS_THREAD_COMM] = A_REVERSE, + [PROCESS_PRIV] = A_BOLD, [BAR_BORDER] = A_BOLD, [BAR_SHADOW] = A_DIM, [SWAP] = A_BOLD, @@ -371,6 +373,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Blue, White), [PROCESS_COMM] = ColorPair(Magenta, White), [PROCESS_THREAD_COMM] = ColorPair(Green, White), + [PROCESS_PRIV] = ColorPair(Magenta, White), [BAR_BORDER] = ColorPair(Blue, White), [BAR_SHADOW] = ColorPair(Black, White), [SWAP] = ColorPair(Red, White), @@ -478,6 +481,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Blue, Black), [PROCESS_COMM] = ColorPair(Magenta, Black), [PROCESS_THREAD_COMM] = ColorPair(Yellow, Black), + [PROCESS_PRIV] = ColorPair(Magenta, Black), [BAR_BORDER] = ColorPair(Blue, Black), [BAR_SHADOW] = ColorPairGrayBlack, [SWAP] = ColorPair(Red, Black), @@ -585,6 +589,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Green, Blue), [PROCESS_COMM] = ColorPair(Magenta, Blue), [PROCESS_THREAD_COMM] = ColorPair(Black, Blue), + [PROCESS_PRIV] = ColorPair(Magenta, Blue), [BAR_BORDER] = A_BOLD | ColorPair(Yellow, Blue), [BAR_SHADOW] = ColorPair(Cyan, Blue), [SWAP] = ColorPair(Red, Blue), @@ -692,6 +697,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [PROCESS_LOW_PRIORITY] = ColorPair(Green, Black), [PROCESS_NEW] = ColorPair(Black, Green), [PROCESS_TOMB] = ColorPair(Black, Red), + [PROCESS_PRIV] = ColorPair(Magenta, Black), [BAR_BORDER] = A_BOLD | ColorPair(Green, Black), [BAR_SHADOW] = ColorPair(Cyan, Black), [SWAP] = ColorPair(Red, Black), diff --git a/CRT.h b/CRT.h index c06d3ae7f..bf5e460e4 100644 --- a/CRT.h +++ b/CRT.h @@ -85,6 +85,7 @@ typedef enum ColorElements_ { PROCESS_THREAD_BASENAME, PROCESS_COMM, PROCESS_THREAD_COMM, + PROCESS_PRIV, BAR_BORDER, BAR_SHADOW, GRAPH_1, diff --git a/Process.c b/Process.c index 614369b40..5a179b424 100644 --- a/Process.c +++ b/Process.c @@ -1020,7 +1020,9 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field } break; case USER: - if (Process_getuid != this->st_uid) + if (this->elevated_priv) + attr = CRT_colors[PROCESS_PRIV]; + else if (Process_getuid != this->st_uid) attr = CRT_colors[PROCESS_SHADOW]; if (this->user) { diff --git a/Process.h b/Process.h index 0fdc392be..db32ecc17 100644 --- a/Process.h +++ b/Process.h @@ -151,6 +151,13 @@ typedef struct Process_ { /* User name */ const char* user; + /* Non root owned process with elevated privileges + * Linux: + * - from file capabilities + * - inherited from the ambient set + */ + bool elevated_priv; + /* Process runtime (in hundredth of a second) */ unsigned long long int time; diff --git a/linux/LinuxProcessList.c b/linux/LinuxProcessList.c index 0576dd67e..24b7ea98f 100644 --- a/linux/LinuxProcessList.c +++ b/linux/LinuxProcessList.c @@ -568,6 +568,14 @@ static bool LinuxProcessList_readStatusFile(Process* process, openat_arg_t procF if (pid_ns_count > 1) process->isRunningInContainer = true; + } else if (String_startsWith(buffer, "CapPrm:")) { + char* ptr = buffer + strlen("CapPrm:"); + while (*ptr == ' ' || *ptr == '\t') + ptr++; + + uint64_t cap_permitted = fast_strtoull_hex(&ptr, 16); + process->elevated_priv = cap_permitted != 0 && process->st_uid != 0; + } else if (String_startsWith(buffer, "voluntary_ctxt_switches:")) { unsigned long vctxt; int ok = sscanf(buffer, "voluntary_ctxt_switches:\t%lu", &vctxt); From 0bd10858a1768b975606b58bbf4a0c6de26aceef Mon Sep 17 00:00:00 2001 From: Matthias Maier Date: Mon, 13 Feb 2023 21:39:32 -0600 Subject: [PATCH 043/106] Linux: add IRQ PSI meter The linux kernel recently gained a new PSI meter, namely a new /proc/pressure/irq meter has been added [1]: full avg10=0.00 avg60=0.00 avg300=0.00 total=100648410 This commit adds support for this PSI meter by adding a PressureStallIRQFullMeter_class. [1] https://github.com/torvalds/linux/commit/52b1364ba0b105122d6de0e719b36db705011ac1 --- linux/Platform.c | 1 + linux/PressureStallMeter.c | 19 +++++++++++++++++++ linux/PressureStallMeter.h | 2 ++ 3 files changed, 22 insertions(+) diff --git a/linux/Platform.c b/linux/Platform.c index 12852e36b..ad94381e6 100644 --- a/linux/Platform.c +++ b/linux/Platform.c @@ -240,6 +240,7 @@ const MeterClass* const Platform_meterTypes[] = { &PressureStallCPUSomeMeter_class, &PressureStallIOSomeMeter_class, &PressureStallIOFullMeter_class, + &PressureStallIRQFullMeter_class, &PressureStallMemorySomeMeter_class, &PressureStallMemoryFullMeter_class, &ZfsArcMeter_class, diff --git a/linux/PressureStallMeter.c b/linux/PressureStallMeter.c index e5089fcc4..c4f534ea5 100644 --- a/linux/PressureStallMeter.c +++ b/linux/PressureStallMeter.c @@ -31,6 +31,8 @@ static void PressureStallMeter_updateValues(Meter* this) { file = "cpu"; } else if (strstr(Meter_name(this), "IO")) { file = "io"; + } else if (strstr(Meter_name(this), "IRQ")) { + file = "irq"; } else { file = "memory"; } @@ -114,6 +116,23 @@ const MeterClass PressureStallIOFullMeter_class = { .description = "Pressure Stall Information, full io" }; +const MeterClass PressureStallIRQFullMeter_class = { + .super = { + .extends = Class(Meter), + .delete = Meter_delete, + .display = PressureStallMeter_display, + }, + .updateValues = PressureStallMeter_updateValues, + .defaultMode = TEXT_METERMODE, + .maxItems = 3, + .total = 100.0, + .attributes = PressureStallMeter_attributes, + .name = "PressureStallIRQFull", + .uiName = "PSI full IRQ", + .caption = "PSI full IRQ: ", + .description = "Pressure Stall Information, full irq" +}; + const MeterClass PressureStallMemorySomeMeter_class = { .super = { .extends = Class(Meter), diff --git a/linux/PressureStallMeter.h b/linux/PressureStallMeter.h index 8acf46bca..93ebd27ca 100644 --- a/linux/PressureStallMeter.h +++ b/linux/PressureStallMeter.h @@ -19,6 +19,8 @@ extern const MeterClass PressureStallIOSomeMeter_class; extern const MeterClass PressureStallIOFullMeter_class; +extern const MeterClass PressureStallIRQFullMeter_class; + extern const MeterClass PressureStallMemorySomeMeter_class; extern const MeterClass PressureStallMemoryFullMeter_class; From 6d1c5de5188f5b00763113ede48c2371027ab012 Mon Sep 17 00:00:00 2001 From: Nathan Scott Date: Tue, 14 Feb 2023 16:13:02 +1100 Subject: [PATCH 044/106] PCP: add IRQ PSI meter --- pcp/PCPMetric.h | 1 + pcp/Platform.c | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/pcp/PCPMetric.h b/pcp/PCPMetric.h index 84ccbb95c..c0966b388 100644 --- a/pcp/PCPMetric.h +++ b/pcp/PCPMetric.h @@ -75,6 +75,7 @@ typedef enum PCPMetric_ { PCP_PSI_CPUSOME, /* kernel.all.pressure.cpu.some.avg */ PCP_PSI_IOSOME, /* kernel.all.pressure.io.some.avg */ PCP_PSI_IOFULL, /* kernel.all.pressure.io.full.avg */ + PCP_PSI_IRQFULL, /* kernel.all.pressure.irq.full.avg */ PCP_PSI_MEMSOME, /* kernel.all.pressure.memory.some.avg */ PCP_PSI_MEMFULL, /* kernel.all.pressure.memory.full.avg */ PCP_ZFS_ARC_ANON_SIZE, /* zfs.arc.anon_size */ diff --git a/pcp/Platform.c b/pcp/Platform.c index 994cef3c6..d9f75ebc5 100644 --- a/pcp/Platform.c +++ b/pcp/Platform.c @@ -106,6 +106,7 @@ const MeterClass* const Platform_meterTypes[] = { &PressureStallCPUSomeMeter_class, &PressureStallIOSomeMeter_class, &PressureStallIOFullMeter_class, + &PressureStallIRQFullMeter_class, &PressureStallMemorySomeMeter_class, &PressureStallMemoryFullMeter_class, &ZfsArcMeter_class, @@ -171,6 +172,7 @@ static const char* Platform_metricNames[] = { [PCP_PSI_CPUSOME] = "kernel.all.pressure.cpu.some.avg", [PCP_PSI_IOSOME] = "kernel.all.pressure.io.some.avg", [PCP_PSI_IOFULL] = "kernel.all.pressure.io.full.avg", + [PCP_PSI_IRQFULL] = "kernel.all.pressure.irq.full.avg", [PCP_PSI_MEMSOME] = "kernel.all.pressure.memory.some.avg", [PCP_PSI_MEMFULL] = "kernel.all.pressure.memory.full.avg", @@ -682,6 +684,8 @@ void Platform_getPressureStall(const char* file, bool some, double* ten, double* metric = PCP_PSI_CPUSOME; else if (String_eq(file, "io")) metric = some ? PCP_PSI_IOSOME : PCP_PSI_IOFULL; + else if (String_eq(file, "irq")) + metric = PCP_PSI_IRQFULL; else if (String_eq(file, "mem")) metric = some ? PCP_PSI_MEMSOME : PCP_PSI_MEMFULL; else From 37eddcf5cd5129040532d5f9b965006226fdb0c1 Mon Sep 17 00:00:00 2001 From: Daniel Lange Date: Thu, 16 Feb 2023 12:50:26 +0100 Subject: [PATCH 045/106] Document e3481a98 in man page (Linux: highlight username if process has elevated privileges) --- htop.1.in | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/htop.1.in b/htop.1.in index eefc37f52..72a98fbe8 100644 --- a/htop.1.in +++ b/htop.1.in @@ -449,6 +449,11 @@ resident memory size, see M_RESIDENT above). .B USER The username of the process owner, or the user ID if the name can't be determined. + +On Linux the username is highlighted if the process has elevated privileges, +i.e. if it has been started from binaries with file capabilities set or +retained Linux capabilities, via the ambient set, after switching from the +root user. .TP .B TIME (TIME+) The time, measured in clock ticks that the process has spent in user and system From 7c68f01ddd9a118709717d990827c90fbf768447 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= Date: Sat, 4 Feb 2023 17:26:53 +0100 Subject: [PATCH 046/106] Misc cleanup * declare read-only pointer parameter const * drop duplicate include of * use String_startsWith --- ScreenManager.c | 2 +- linux/Platform.c | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/ScreenManager.c b/ScreenManager.c index 55cacd20a..87e718926 100644 --- a/ScreenManager.c +++ b/ScreenManager.c @@ -153,7 +153,7 @@ static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTi *rescan = false; } -static inline bool drawTab(int* y, int* x, int l, const char* name, bool cur) { +static inline bool drawTab(const int* y, int* x, int l, const char* name, bool cur) { attrset(CRT_colors[cur ? SCREENS_CUR_BORDER : SCREENS_OTH_BORDER]); mvaddch(*y, *x, '['); (*x)++; diff --git a/linux/Platform.c b/linux/Platform.c index ad94381e6..911284e9b 100644 --- a/linux/Platform.c +++ b/linux/Platform.c @@ -64,7 +64,6 @@ in the source distribution for its full text. #include "zfs/ZfsCompressedArcMeter.h" #ifdef HAVE_LIBCAP -#include #include #endif @@ -476,7 +475,7 @@ FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) { if (!strchr(buffer, '\n')) continue; - if (strncmp(buffer, "lock:\t", strlen("lock:\t"))) + if (!String_startsWith(buffer, "lock:\t")) continue; FileLocks_Data data = {.fd = file}; From f60d4057573ecb6a5021e754f54983c709977c62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= Date: Sat, 18 Feb 2023 16:13:26 +0100 Subject: [PATCH 047/106] ci: install pkg-config on MacOS --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2e75ec091..8e4ea0652 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -151,7 +151,7 @@ jobs: steps: - uses: actions/checkout@v3 - name: Install Dependencies - run: brew install automake + run: brew install automake pkg-config - name: Bootstrap run: ./autogen.sh - name: Configure From 8387df1551f18b90b3936c7cbb311416c3db8032 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= Date: Sat, 4 Feb 2023 17:34:08 +0100 Subject: [PATCH 048/106] Annotate functions with access attribute Supported by GCC since version 10. --- Macros.h | 21 +++++++++++++++++++++ XUtils.c | 1 + XUtils.h | 7 +++++++ configure.ac | 14 ++++++++++++++ 4 files changed, 43 insertions(+) diff --git a/Macros.h b/Macros.h index 5e8891a41..0f95347b7 100644 --- a/Macros.h +++ b/Macros.h @@ -61,6 +61,27 @@ #endif /* HAVE_ATTR_ALLOC_SIZE */ +#ifdef HAVE_ATTR_ACCESS + +#define ATTR_ACCESS2(mode, ref) __attribute__((access (mode, ref))) +#define ATTR_ACCESS3(mode, ref, size) __attribute__((access (mode, ref, size))) + +#else + +#define ATTR_ACCESS2(mode, ref) +#define ATTR_ACCESS3(mode, ref, size) + +#endif /* HAVE_ATTR_ACCESS */ + +#define ATTR_ACCESS2_R(ref) ATTR_ACCESS2(read_only, ref) +#define ATTR_ACCESS3_R(ref, size) ATTR_ACCESS3(read_only, ref, size) + +#define ATTR_ACCESS2_RW(ref) ATTR_ACCESS2(read_write, ref) +#define ATTR_ACCESS3_RW(ref, size) ATTR_ACCESS3(read_write, ref, size) + +#define ATTR_ACCESS2_W(ref) ATTR_ACCESS2(write_only, ref) +#define ATTR_ACCESS3_W(ref, size) ATTR_ACCESS3(write_only, ref, size) + // ignore casts discarding const specifier, e.g. // const char [] -> char * / void * // const char *[2]' -> char *const * diff --git a/XUtils.c b/XUtils.c index 6a021f35b..f54ad49f8 100644 --- a/XUtils.c +++ b/XUtils.c @@ -265,6 +265,7 @@ char* xStrndup(const char* str, size_t len) { return data; } +ATTR_ACCESS3_W(2, 3) static ssize_t readfd_internal(int fd, void* buffer, size_t count) { if (!count) { close(fd); diff --git a/XUtils.h b/XUtils.h index e1c50be10..64583db8b 100644 --- a/XUtils.h +++ b/XUtils.h @@ -57,22 +57,29 @@ void String_freeArray(char** s); char* String_readLine(FILE* fd) ATTR_MALLOC; /* Always null-terminates dest. Caller must pass a strictly positive size. */ +ATTR_ACCESS3_W(1, 3) +ATTR_ACCESS3_R(2, 3) size_t String_safeStrncpy(char* restrict dest, const char* restrict src, size_t size); ATTR_FORMAT(printf, 2, 3) int xAsprintf(char** strp, const char* fmt, ...); ATTR_FORMAT(printf, 3, 4) +ATTR_ACCESS3_W(1, 2) int xSnprintf(char* buf, size_t len, const char* fmt, ...); char* xStrdup(const char* str) ATTR_NONNULL ATTR_MALLOC; void free_and_xStrdup(char** ptr, const char* str); +ATTR_ACCESS3_R(1, 2) char* xStrndup(const char* str, size_t len) ATTR_NONNULL ATTR_MALLOC; +ATTR_ACCESS3_W(2, 3) ssize_t xReadfile(const char* pathname, void* buffer, size_t count); +ATTR_ACCESS3_W(3, 4) ssize_t xReadfileat(openat_arg_t dirfd, const char* pathname, void* buffer, size_t count); +ATTR_ACCESS3_R(2, 3) ssize_t full_write(int fd, const void* buf, size_t count); #endif diff --git a/configure.ac b/configure.ac index 73a8e91c8..015f90461 100644 --- a/configure.ac +++ b/configure.ac @@ -188,6 +188,20 @@ AC_COMPILE_IFELSE([ AC_MSG_RESULT(no)) CFLAGS="$old_CFLAGS" +AC_MSG_CHECKING(for access) +old_CFLAGS="$CFLAGS" +CFLAGS="$CFLAGS -Wno-error -Werror=attributes" +AC_COMPILE_IFELSE([ + AC_LANG_SOURCE( + [ + __attribute__((access(read_only, 1, 2))) extern int foo(const char* str, unsigned len); + ],[] + )], + AC_DEFINE([HAVE_ATTR_ACCESS], 1, [The access attribute is supported.]) + AC_MSG_RESULT(yes), + AC_MSG_RESULT(no)) +CFLAGS="$old_CFLAGS" + AC_MSG_CHECKING(for NaN support) AC_RUN_IFELSE([ AC_LANG_PROGRAM( From e0229b205c5dceb10447457c8f78acb16c96505a Mon Sep 17 00:00:00 2001 From: Benny Baumann Date: Sun, 1 May 2022 22:43:00 +0200 Subject: [PATCH 049/106] Create new File Descriptor meter --- CRT.c | 12 +++++++ CRT.h | 2 ++ FileDescriptorMeter.c | 80 +++++++++++++++++++++++++++++++++++++++++++ FileDescriptorMeter.h | 15 ++++++++ Makefile.am | 2 ++ 5 files changed, 111 insertions(+) create mode 100644 FileDescriptorMeter.c create mode 100644 FileDescriptorMeter.h diff --git a/CRT.c b/CRT.c index e35608b6c..3c062919d 100644 --- a/CRT.c +++ b/CRT.c @@ -203,6 +203,8 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [PRESSURE_STALL_THREEHUNDRED] = ColorPair(Cyan, Black), [PRESSURE_STALL_SIXTY] = A_BOLD | ColorPair(Cyan, Black), [PRESSURE_STALL_TEN] = A_BOLD | ColorPair(White, Black), + [FILE_DESCRIPTOR_USED] = ColorPair(Green, Black), + [FILE_DESCRIPTOR_MAX] = A_BOLD | ColorPair(Blue, Black), [ZFS_MFU] = A_BOLD | ColorPair(Blue, Black), [ZFS_MRU] = ColorPair(Yellow, Black), [ZFS_ANON] = ColorPair(Magenta, Black), @@ -311,6 +313,8 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [PRESSURE_STALL_THREEHUNDRED] = A_DIM, [PRESSURE_STALL_SIXTY] = A_NORMAL, [PRESSURE_STALL_TEN] = A_BOLD, + [FILE_DESCRIPTOR_USED] = A_BOLD, + [FILE_DESCRIPTOR_MAX] = A_BOLD, [ZFS_MFU] = A_NORMAL, [ZFS_MRU] = A_NORMAL, [ZFS_ANON] = A_DIM, @@ -419,6 +423,8 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [PRESSURE_STALL_THREEHUNDRED] = ColorPair(Black, White), [PRESSURE_STALL_SIXTY] = ColorPair(Black, White), [PRESSURE_STALL_TEN] = ColorPair(Black, White), + [FILE_DESCRIPTOR_USED] = ColorPair(Green, White), + [FILE_DESCRIPTOR_MAX] = ColorPair(Blue, White), [ZFS_MFU] = ColorPair(Cyan, White), [ZFS_MRU] = ColorPair(Yellow, White), [ZFS_ANON] = ColorPair(Magenta, White), @@ -527,6 +533,8 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [PRESSURE_STALL_THREEHUNDRED] = ColorPair(Black, Black), [PRESSURE_STALL_SIXTY] = ColorPair(Black, Black), [PRESSURE_STALL_TEN] = ColorPair(Black, Black), + [FILE_DESCRIPTOR_USED] = ColorPair(Green, Black), + [FILE_DESCRIPTOR_MAX] = A_BOLD | ColorPair(Blue, Black), [ZFS_MFU] = ColorPair(Cyan, Black), [ZFS_MRU] = ColorPair(Yellow, Black), [ZFS_ANON] = A_BOLD | ColorPair(Magenta, Black), @@ -635,6 +643,8 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [PRESSURE_STALL_THREEHUNDRED] = A_BOLD | ColorPair(Black, Blue), [PRESSURE_STALL_SIXTY] = A_NORMAL | ColorPair(White, Blue), [PRESSURE_STALL_TEN] = A_BOLD | ColorPair(White, Blue), + [FILE_DESCRIPTOR_USED] = A_BOLD | ColorPair(Green, Blue), + [FILE_DESCRIPTOR_MAX] = A_BOLD | ColorPair(Red, Blue), [ZFS_MFU] = A_BOLD | ColorPair(White, Blue), [ZFS_MRU] = A_BOLD | ColorPair(Yellow, Blue), [ZFS_ANON] = A_BOLD | ColorPair(Magenta, Blue), @@ -741,6 +751,8 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [PRESSURE_STALL_THREEHUNDRED] = ColorPair(Green, Black), [PRESSURE_STALL_SIXTY] = ColorPair(Green, Black), [PRESSURE_STALL_TEN] = A_BOLD | ColorPair(Green, Black), + [FILE_DESCRIPTOR_USED] = ColorPair(Green, Black), + [FILE_DESCRIPTOR_MAX] = A_BOLD | ColorPair(Blue, Black), [ZFS_MFU] = ColorPair(Blue, Black), [ZFS_MRU] = ColorPair(Yellow, Black), [ZFS_ANON] = ColorPair(Magenta, Black), diff --git a/CRT.h b/CRT.h index bf5e460e4..80de73136 100644 --- a/CRT.h +++ b/CRT.h @@ -129,6 +129,8 @@ typedef enum ColorElements_ { PRESSURE_STALL_TEN, PRESSURE_STALL_SIXTY, PRESSURE_STALL_THREEHUNDRED, + FILE_DESCRIPTOR_USED, + FILE_DESCRIPTOR_MAX, ZFS_MFU, ZFS_MRU, ZFS_ANON, diff --git a/FileDescriptorMeter.c b/FileDescriptorMeter.c new file mode 100644 index 000000000..00d088c8b --- /dev/null +++ b/FileDescriptorMeter.c @@ -0,0 +1,80 @@ +/* +htop - FileDescriptorMeter.c +(C) 2022 htop dev team +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "FileDescriptorMeter.h" + +#include +#include +#include + +#include "CRT.h" +#include "Meter.h" +#include "Object.h" +#include "Platform.h" +#include "RichString.h" +#include "XUtils.h" + + +static const int FileDescriptorMeter_attributes[] = { + FILE_DESCRIPTOR_USED, + FILE_DESCRIPTOR_MAX +}; + +static void FileDescriptorMeter_updateValues(Meter* this) { + this->values[0] = 0; + this->values[1] = 1; + + Platform_getFileDescriptors(&this->values[0], &this->values[1]); + + /* only print bar for first value */ + this->curItems = 1; + + /* Use maximum value for scaling of bar mode */ + this->total = this->values[1]; + + if (isnan(this->values[0])) { + xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "unknown/unknown"); + } else { + xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%.0lf/%.0lf", this->values[0], this->values[1]); + } +} + +static void FileDescriptorMeter_display(const Object* cast, RichString* out) { + const Meter* this = (const Meter*)cast; + char buffer[50]; + int len; + + if (isnan(this->values[0])) { + RichString_appendAscii(out, CRT_colors[METER_TEXT], "unknown"); + return; + } + + RichString_appendAscii(out, CRT_colors[METER_TEXT], "used: "); + len = xSnprintf(buffer, sizeof(buffer), "%.0lf", this->values[0]); + RichString_appendnAscii(out, CRT_colors[FILE_DESCRIPTOR_USED], buffer, len); + + RichString_appendAscii(out, CRT_colors[METER_TEXT], " max: "); + len = xSnprintf(buffer, sizeof(buffer), "%.0lf", this->values[1]); + RichString_appendnAscii(out, CRT_colors[FILE_DESCRIPTOR_MAX], buffer, len); +} + +const MeterClass FileDescriptorMeter_class = { + .super = { + .extends = Class(Meter), + .delete = Meter_delete, + .display = FileDescriptorMeter_display, + }, + .updateValues = FileDescriptorMeter_updateValues, + .defaultMode = TEXT_METERMODE, + .maxItems = 2, + .total = 100.0, + .attributes = FileDescriptorMeter_attributes, + .name = "FileDescriptors", + .uiName = "File Descriptors", + .caption = "FDs: ", + .description = "Number of allocated/available file descriptors" +}; diff --git a/FileDescriptorMeter.h b/FileDescriptorMeter.h new file mode 100644 index 000000000..e1b4f5f8c --- /dev/null +++ b/FileDescriptorMeter.h @@ -0,0 +1,15 @@ +#ifndef HEADER_FileDescriptorMeter +#define HEADER_FileDescriptorMeter +/* +htop - FileDescriptorMeter.h +(C) 2022 htop dev team +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "Meter.h" + + +extern const MeterClass FileDescriptorMeter_class; + +#endif diff --git a/Makefile.am b/Makefile.am index 1c685e4d3..749b0c249 100644 --- a/Makefile.am +++ b/Makefile.am @@ -50,6 +50,7 @@ myhtopsources = \ DynamicColumn.c \ DynamicMeter.c \ EnvScreen.c \ + FileDescriptorMeter.c \ FunctionBar.c \ Hashtable.c \ Header.c \ @@ -110,6 +111,7 @@ myhtopheaders = \ DynamicColumn.h \ DynamicMeter.h \ EnvScreen.h \ + FileDescriptorMeter.h \ FunctionBar.h \ Hashtable.h \ Header.h \ From 3519b383c705de6c41bdec7b87367d5e3c88ec02 Mon Sep 17 00:00:00 2001 From: Benny Baumann Date: Tue, 14 Feb 2023 00:23:22 +0100 Subject: [PATCH 050/106] Implement File Descriptor Meter support for the Unsupported (demo platform) --- unsupported/Platform.c | 7 +++++++ unsupported/Platform.h | 2 ++ 2 files changed, 9 insertions(+) diff --git a/unsupported/Platform.c b/unsupported/Platform.c index 27bc560bd..df7f3600a 100644 --- a/unsupported/Platform.c +++ b/unsupported/Platform.c @@ -16,6 +16,7 @@ in the source distribution for its full text. #include "ClockMeter.h" #include "DateMeter.h" #include "DateTimeMeter.h" +#include "FileDescriptorMeter.h" #include "HostnameMeter.h" #include "LoadAverageMeter.h" #include "Macros.h" @@ -70,6 +71,7 @@ const MeterClass* const Platform_meterTypes[] = { &RightCPUs4Meter_class, &LeftCPUs8Meter_class, &RightCPUs8Meter_class, + &FileDescriptorMeter_class, &BlankMeter_class, NULL }; @@ -134,6 +136,11 @@ FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) { return NULL; } +void Platform_getFileDescriptors(double* used, double* max) { + *used = 1337; + *max = 4711; +} + bool Platform_getDiskIO(DiskIOData* data) { (void)data; return false; diff --git a/unsupported/Platform.h b/unsupported/Platform.h index f475dda4d..a718ca09e 100644 --- a/unsupported/Platform.h +++ b/unsupported/Platform.h @@ -52,6 +52,8 @@ char* Platform_getProcessEnv(pid_t pid); FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid); +void Platform_getFileDescriptors(double* used, double* max); + bool Platform_getDiskIO(DiskIOData* data); bool Platform_getNetworkIO(NetworkIOData* data); From 62f4628efef67280c7c480dcd5df65867bd4d960 Mon Sep 17 00:00:00 2001 From: Benny Baumann Date: Mon, 13 Feb 2023 22:42:42 +0100 Subject: [PATCH 051/106] Implement File Descriptor Meter support for Linux --- linux/Platform.c | 20 ++++++++++++++++++++ linux/Platform.h | 2 ++ 2 files changed, 22 insertions(+) diff --git a/linux/Platform.c b/linux/Platform.c index 911284e9b..005d8316b 100644 --- a/linux/Platform.c +++ b/linux/Platform.c @@ -31,6 +31,7 @@ in the source distribution for its full text. #include "DateMeter.h" #include "DateTimeMeter.h" #include "DiskIOMeter.h" +#include "FileDescriptorMeter.h" #include "HostnameMeter.h" #include "HugePageMeter.h" #include "LoadAverageMeter.h" @@ -250,6 +251,7 @@ const MeterClass* const Platform_meterTypes[] = { &SELinuxMeter_class, &SystemdMeter_class, &SystemdUserMeter_class, + &FileDescriptorMeter_class, NULL }; @@ -538,6 +540,24 @@ void Platform_getPressureStall(const char* file, bool some, double* ten, double* fclose(fd); } +void Platform_getFileDescriptors(double* used, double* max) { + *used = NAN; + *max = 65536; + + FILE* fd = fopen(PROCDIR "/sys/fs/file-nr", "r"); + if (!fd) + return; + + unsigned long long v1, v2, v3; + int total = fscanf(fd, "%llu %llu %llu", &v1, &v2, &v3); + if (total == 3) { + *used = v1; + *max = v3; + } + + fclose(fd); +} + bool Platform_getDiskIO(DiskIOData* data) { FILE* fd = fopen(PROCDIR "/diskstats", "r"); if (!fd) diff --git a/linux/Platform.h b/linux/Platform.h index f6ac18804..1621d5628 100644 --- a/linux/Platform.h +++ b/linux/Platform.h @@ -79,6 +79,8 @@ FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid); void Platform_getPressureStall(const char* file, bool some, double* ten, double* sixty, double* threehundred); +void Platform_getFileDescriptors(double* used, double* max); + bool Platform_getDiskIO(DiskIOData* data); bool Platform_getNetworkIO(NetworkIOData* data); From bdb63a497beeb4b317296ca123c507233a2af889 Mon Sep 17 00:00:00 2001 From: Sohaib Mohamed Date: Wed, 4 May 2022 09:55:43 +0200 Subject: [PATCH 052/106] Implement File Descriptor Meter support for PCP Signed-off-by: Sohaib Mohamed --- pcp/PCPMetric.h | 2 ++ pcp/Platform.c | 15 +++++++++++++++ pcp/Platform.h | 2 ++ 3 files changed, 19 insertions(+) diff --git a/pcp/PCPMetric.h b/pcp/PCPMetric.h index c0966b388..e89a0a4c7 100644 --- a/pcp/PCPMetric.h +++ b/pcp/PCPMetric.h @@ -93,6 +93,8 @@ typedef enum PCPMetric_ { PCP_ZRAM_CAPACITY, /* zram.capacity */ PCP_ZRAM_ORIGINAL, /* zram.mm_stat.data_size.original */ PCP_ZRAM_COMPRESSED, /* zram.mm_stat.data_size.compressed */ + PCP_VFS_FILES_COUNT, /* vfs.files.count */ + PCP_VFS_FILES_MAX, /* vfs.files.max */ PCP_PROC_PID, /* proc.psinfo.pid */ PCP_PROC_PPID, /* proc.psinfo.ppid */ diff --git a/pcp/Platform.c b/pcp/Platform.c index d9f75ebc5..a76c0288a 100644 --- a/pcp/Platform.c +++ b/pcp/Platform.c @@ -25,6 +25,7 @@ in the source distribution for its full text. #include "DiskIOMeter.h" #include "DynamicColumn.h" #include "DynamicMeter.h" +#include "FileDescriptorMeter.h" #include "HostnameMeter.h" #include "LoadAverageMeter.h" #include "Macros.h" @@ -115,6 +116,7 @@ const MeterClass* const Platform_meterTypes[] = { &DiskIOMeter_class, &NetworkIOMeter_class, &SysArchMeter_class, + &FileDescriptorMeter_class, NULL }; @@ -192,6 +194,8 @@ static const char* Platform_metricNames[] = { [PCP_ZRAM_CAPACITY] = "zram.capacity", [PCP_ZRAM_ORIGINAL] = "zram.mm_stat.data_size.original", [PCP_ZRAM_COMPRESSED] = "zram.mm_stat.data_size.compressed", + [PCP_VFS_FILES_COUNT] = "vfs.files.count", + [PCP_VFS_FILES_MAX] = "vfs.files.max", [PCP_PROC_PID] = "proc.psinfo.pid", [PCP_PROC_PPID] = "proc.psinfo.ppid", @@ -727,6 +731,17 @@ bool Platform_getNetworkIO(NetworkIOData* data) { return true; } +void Platform_getFileDescriptors(double* used, double* max) { + *used = NAN; + *max = 65536; + + pmAtomValue value; + if (PCPMetric_values(PCP_VFS_FILES_COUNT, &value, 1, PM_TYPE_32) != NULL) + *used = value.l; + if (PCPMetric_values(PCP_VFS_FILES_MAX, &value, 1, PM_TYPE_32) != NULL) + *max = value.l; +} + void Platform_getBattery(double* level, ACPresence* isOnAC) { *level = NAN; *isOnAC = AC_ERROR; diff --git a/pcp/Platform.h b/pcp/Platform.h index f06f22664..f90e28135 100644 --- a/pcp/Platform.h +++ b/pcp/Platform.h @@ -131,6 +131,8 @@ extern pmOptions opts; size_t Platform_addMetric(PCPMetric id, const char* name); +void Platform_getFileDescriptors(double* used, double* max); + void Platform_gettime_realtime(struct timeval* tv, uint64_t* msec); void Platform_gettime_monotonic(uint64_t* msec); From 377a06db0de3850573f2a8e2c33152cf3483ea61 Mon Sep 17 00:00:00 2001 From: Benny Baumann Date: Mon, 13 Feb 2023 22:35:54 +0100 Subject: [PATCH 053/106] Implement File Descriptor Meter support for OpenBSD --- openbsd/Platform.c | 26 ++++++++++++++++++++++++++ openbsd/Platform.h | 2 ++ 2 files changed, 28 insertions(+) diff --git a/openbsd/Platform.c b/openbsd/Platform.c index 1ce5ba194..71419f8eb 100644 --- a/openbsd/Platform.c +++ b/openbsd/Platform.c @@ -28,6 +28,7 @@ in the source distribution for its full text. #include "ClockMeter.h" #include "DateMeter.h" #include "DateTimeMeter.h" +#include "FileDescriptorMeter.h" #include "HostnameMeter.h" #include "LoadAverageMeter.h" #include "Macros.h" @@ -125,6 +126,7 @@ const MeterClass* const Platform_meterTypes[] = { &RightCPUs4Meter_class, &LeftCPUs8Meter_class, &RightCPUs8Meter_class, + &FileDescriptorMeter_class, &BlankMeter_class, NULL }; @@ -301,6 +303,30 @@ FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) { return NULL; } +void Platform_getFileDescriptors(double* used, double* max) { + static const int mib_kern_maxfile[] = { CTL_KERN, KERN_MAXFILES }; + int sysctl_maxfile = 0; + size_t size_maxfile = sizeof(int); + if (sysctl(mib_kern_maxfile, ARRAYSIZE(mib_kern_maxfile), &sysctl_maxfile, &size_maxfile, NULL, 0) < 0) { + *max = NAN; + } else if (size_maxfile != sizeof(int) || sysctl_maxfile < 1) { + *max = NAN; + } else { + *max = sysctl_maxfile; + } + + static const int mib_kern_nfiles[] = { CTL_KERN, KERN_NFILES }; + int sysctl_nfiles = 0; + size_t size_nfiles = sizeof(int); + if (sysctl(mib_kern_nfiles, ARRAYSIZE(mib_kern_nfiles), &sysctl_nfiles, &size_nfiles, NULL, 0) < 0) { + *used = NAN; + } else if (size_nfiles != sizeof(int) || sysctl_nfiles < 0) { + *used = NAN; + } else { + *used = sysctl_nfiles; + } +} + bool Platform_getDiskIO(DiskIOData* data) { // TODO (void)data; diff --git a/openbsd/Platform.h b/openbsd/Platform.h index 27d792e0d..f357006cc 100644 --- a/openbsd/Platform.h +++ b/openbsd/Platform.h @@ -59,6 +59,8 @@ char* Platform_getProcessEnv(pid_t pid); FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid); +void Platform_getFileDescriptors(double* used, double* max); + bool Platform_getDiskIO(DiskIOData* data); bool Platform_getNetworkIO(NetworkIOData* data); From 7a8c01f304b673dd89923e05d242aebb9cd0a500 Mon Sep 17 00:00:00 2001 From: Benny Baumann Date: Sun, 5 Feb 2023 02:40:42 +0100 Subject: [PATCH 054/106] Implement File Descriptor Meter support for DragonflyBSD/FreeBSD/NetBSD --- Makefile.am | 6 +++ dragonflybsd/Platform.c | 8 ++++ dragonflybsd/Platform.h | 2 + freebsd/Platform.c | 7 ++++ freebsd/Platform.h | 2 + generic/fdstat_sysctl.c | 83 +++++++++++++++++++++++++++++++++++++++++ generic/fdstat_sysctl.h | 13 +++++++ netbsd/Platform.c | 7 ++++ netbsd/Platform.h | 2 + 9 files changed, 130 insertions(+) create mode 100644 generic/fdstat_sysctl.c create mode 100644 generic/fdstat_sysctl.h diff --git a/Makefile.am b/Makefile.am index 749b0c249..86b747a01 100644 --- a/Makefile.am +++ b/Makefile.am @@ -209,6 +209,7 @@ freebsd_platform_headers = \ freebsd/FreeBSDProcess.h \ freebsd/Platform.h \ freebsd/ProcessField.h \ + generic/fdstat_sysctl.h \ generic/gettime.h \ generic/hostname.h \ generic/openzfs_sysctl.h \ @@ -221,6 +222,7 @@ freebsd_platform_sources = \ freebsd/Platform.c \ freebsd/FreeBSDProcessList.c \ freebsd/FreeBSDProcess.c \ + generic/fdstat_sysctl.c \ generic/gettime.c \ generic/hostname.c \ generic/openzfs_sysctl.c \ @@ -241,6 +243,7 @@ dragonflybsd_platform_headers = \ dragonflybsd/DragonFlyBSDProcess.h \ dragonflybsd/Platform.h \ dragonflybsd/ProcessField.h \ + generic/fdstat_sysctl.h \ generic/gettime.h \ generic/hostname.h \ generic/uname.h @@ -249,6 +252,7 @@ dragonflybsd_platform_sources = \ dragonflybsd/DragonFlyBSDProcessList.c \ dragonflybsd/DragonFlyBSDProcess.c \ dragonflybsd/Platform.c \ + generic/fdstat_sysctl.c \ generic/gettime.c \ generic/hostname.c \ generic/uname.c @@ -262,6 +266,7 @@ endif # ------- netbsd_platform_headers = \ + generic/fdstat_sysctl.h \ generic/gettime.h \ generic/hostname.h \ generic/uname.h \ @@ -271,6 +276,7 @@ netbsd_platform_headers = \ netbsd/NetBSDProcessList.h netbsd_platform_sources = \ + generic/fdstat_sysctl.c \ generic/gettime.c \ generic/hostname.c \ generic/uname.c \ diff --git a/dragonflybsd/Platform.c b/dragonflybsd/Platform.c index 8a684d866..277f1876f 100644 --- a/dragonflybsd/Platform.c +++ b/dragonflybsd/Platform.c @@ -20,6 +20,7 @@ in the source distribution for its full text. #include "CPUMeter.h" #include "DateMeter.h" #include "DateTimeMeter.h" +#include "FileDescriptorMeter.h" #include "HostnameMeter.h" #include "LoadAverageMeter.h" #include "MemoryMeter.h" @@ -31,6 +32,8 @@ in the source distribution for its full text. #include "UptimeMeter.h" #include "dragonflybsd/DragonFlyBSDProcess.h" #include "dragonflybsd/DragonFlyBSDProcessList.h" +#include "generic/fdstat_sysctl.h" + const ScreenDefaults Platform_defaultScreens[] = { { @@ -108,6 +111,7 @@ const MeterClass* const Platform_meterTypes[] = { &RightCPUs4Meter_class, &LeftCPUs8Meter_class, &RightCPUs8Meter_class, + &FileDescriptorMeter_class, &BlankMeter_class, NULL }; @@ -233,6 +237,10 @@ FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) { return NULL; } +void Platform_getFileDescriptors(double* used, double* max) { + Generic_getFileDescriptors_sysctl(used, max); +} + bool Platform_getDiskIO(DiskIOData* data) { // TODO (void)data; diff --git a/dragonflybsd/Platform.h b/dragonflybsd/Platform.h index cf6935628..b37cea2a1 100644 --- a/dragonflybsd/Platform.h +++ b/dragonflybsd/Platform.h @@ -61,6 +61,8 @@ char* Platform_getProcessEnv(pid_t pid); FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid); +void Platform_getFileDescriptors(double* used, double* max); + bool Platform_getDiskIO(DiskIOData* data); bool Platform_getNetworkIO(NetworkIOData* data); diff --git a/freebsd/Platform.c b/freebsd/Platform.c index 646163af1..061de70bc 100644 --- a/freebsd/Platform.c +++ b/freebsd/Platform.c @@ -31,6 +31,7 @@ in the source distribution for its full text. #include "DateMeter.h" #include "DateTimeMeter.h" #include "DiskIOMeter.h" +#include "FileDescriptorMeter.h" #include "HostnameMeter.h" #include "LoadAverageMeter.h" #include "Macros.h" @@ -47,6 +48,7 @@ in the source distribution for its full text. #include "XUtils.h" #include "freebsd/FreeBSDProcess.h" #include "freebsd/FreeBSDProcessList.h" +#include "generic/fdstat_sysctl.h" #include "zfs/ZfsArcMeter.h" #include "zfs/ZfsCompressedArcMeter.h" @@ -130,6 +132,7 @@ const MeterClass* const Platform_meterTypes[] = { &ZfsArcMeter_class, &ZfsCompressedArcMeter_class, &DiskIOMeter_class, + &FileDescriptorMeter_class, &NetworkIOMeter_class, NULL }; @@ -292,6 +295,10 @@ FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) { return NULL; } +void Platform_getFileDescriptors(double* used, double* max) { + Generic_getFileDescriptors_sysctl(used, max); +} + bool Platform_getDiskIO(DiskIOData* data) { if (devstat_checkversion(NULL) < 0) diff --git a/freebsd/Platform.h b/freebsd/Platform.h index 51269c091..849f7ddf3 100644 --- a/freebsd/Platform.h +++ b/freebsd/Platform.h @@ -61,6 +61,8 @@ char* Platform_getProcessEnv(pid_t pid); FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid); +void Platform_getFileDescriptors(double* used, double* max); + bool Platform_getDiskIO(DiskIOData* data); bool Platform_getNetworkIO(NetworkIOData* data); diff --git a/generic/fdstat_sysctl.c b/generic/fdstat_sysctl.c new file mode 100644 index 000000000..9e89a0da5 --- /dev/null +++ b/generic/fdstat_sysctl.c @@ -0,0 +1,83 @@ +/* +htop - generic/fdstat_sysctl.c +(C) 2022-2023 htop dev team +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "generic/fdstat_sysctl.h" + +#include +#include +#include + +#include +#include + +#include "config.h" + + +static void Generic_getFileDescriptors_sysctl_internal( + const char* sysctlname_maxfiles, + const char* sysctlname_numfiles, + size_t size_header, + size_t size_entry, + double* used, + double* max +) { + *used = NAN; + *max = 65536; + + int max_fd, open_fd; + size_t len; + + len = sizeof(max_fd); + if (sysctlname_maxfiles && sysctlbyname(sysctlname_maxfiles, &max_fd, &len, NULL, 0) == 0) { + if (max_fd) { + *max = max_fd; + } else { + *max = NAN; + } + } + + len = sizeof(open_fd); + if (sysctlname_numfiles && sysctlbyname(sysctlname_numfiles, &open_fd, &len, NULL, 0) == 0) { + *used = open_fd; + } + + if (!isnan(*used)) { + return; + } + + // If no sysctl arc available, try to guess from the file table size at kern.file + // The size per entry differs per OS, thus skip if we don't know: + if (!size_entry) + return; + + len = 0; + if (sysctlbyname("kern.file", NULL, &len, NULL, 0) < 0) + return; + + if (len < size_header) + return; + + *used = (len - size_header) / size_entry; +} + +void Generic_getFileDescriptors_sysctl(double* used, double* max) { +#if defined(HTOP_DARWIN) + Generic_getFileDescriptors_sysctl_internal( + "kern.maxfiles", "kern.num_files", 0, 0, used, max); +#elif defined(HTOP_DRAGONFLY) + Generic_getFileDescriptors_sysctl_internal( + "kern.maxfiles", NULL, 0, sizeof(struct kinfo_file), used, max); +#elif defined(HTOP_FREEBSD) + Generic_getFileDescriptors_sysctl_internal( + "kern.maxfiles", "kern.openfiles", 0, 0, used, max); +#elif defined(HTOP_NETBSD) + Generic_getFileDescriptors_sysctl_internal( + "kern.maxfiles", NULL, 0, sizeof(struct kinfo_file), used, max); +#else +#error Unknown platform: Please implement proper way to query open/max file information +#endif +} diff --git a/generic/fdstat_sysctl.h b/generic/fdstat_sysctl.h new file mode 100644 index 000000000..107fcabe4 --- /dev/null +++ b/generic/fdstat_sysctl.h @@ -0,0 +1,13 @@ +#ifndef HEADER_fdstat_sysctl +#define HEADER_fdstat_sysctl +/* +htop - generic/fdstat_sysctl.h +(C) 2022-2023 htop dev team +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + + +void Generic_getFileDescriptors_sysctl(double* used, double* max); + +#endif diff --git a/netbsd/Platform.c b/netbsd/Platform.c index ad6050c00..c5c42d0ab 100644 --- a/netbsd/Platform.c +++ b/netbsd/Platform.c @@ -38,6 +38,7 @@ in the source distribution for its full text. #include "ClockMeter.h" #include "DateMeter.h" #include "DateTimeMeter.h" +#include "FileDescriptorMeter.h" #include "HostnameMeter.h" #include "LoadAverageMeter.h" #include "Macros.h" @@ -52,6 +53,7 @@ in the source distribution for its full text. #include "TasksMeter.h" #include "UptimeMeter.h" #include "XUtils.h" +#include "generic/fdstat_sysctl.h" #include "netbsd/NetBSDProcess.h" #include "netbsd/NetBSDProcessList.h" @@ -179,6 +181,7 @@ const MeterClass* const Platform_meterTypes[] = { &BlankMeter_class, &DiskIOMeter_class, &NetworkIOMeter_class, + &FileDescriptorMeter_class, NULL }; @@ -343,6 +346,10 @@ FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) { return NULL; } +void Platform_getFileDescriptors(double* used, double* max) { + Generic_getFileDescriptors_sysctl(used, max); +} + bool Platform_getDiskIO(DiskIOData* data) { const int mib[] = { CTL_HW, HW_IOSTATS, sizeof(struct io_sysctl) }; struct io_sysctl* iostats = NULL; diff --git a/netbsd/Platform.h b/netbsd/Platform.h index 0e53b4570..a75c766cf 100644 --- a/netbsd/Platform.h +++ b/netbsd/Platform.h @@ -67,6 +67,8 @@ char* Platform_getProcessEnv(pid_t pid); FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid); +void Platform_getFileDescriptors(double* used, double* max); + bool Platform_getDiskIO(DiskIOData* data); bool Platform_getNetworkIO(NetworkIOData* data); From 3b6019725defb5b5f516d1abd61906c2398d4bfa Mon Sep 17 00:00:00 2001 From: Benny Baumann Date: Sun, 5 Feb 2023 02:53:28 +0100 Subject: [PATCH 055/106] Implement File Descriptor Meter support for Darwin --- Makefile.am | 2 ++ darwin/Platform.c | 7 +++++++ darwin/Platform.h | 2 ++ 3 files changed, 11 insertions(+) diff --git a/Makefile.am b/Makefile.am index 86b747a01..2a6db44d4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -323,6 +323,7 @@ darwin_platform_headers = \ darwin/Platform.h \ darwin/PlatformHelpers.h \ darwin/ProcessField.h \ + generic/fdstat_sysctl.h \ generic/gettime.h \ generic/hostname.h \ generic/openzfs_sysctl.h \ @@ -336,6 +337,7 @@ darwin_platform_sources = \ darwin/PlatformHelpers.c \ darwin/DarwinProcess.c \ darwin/DarwinProcessList.c \ + generic/fdstat_sysctl.c \ generic/gettime.c \ generic/hostname.c \ generic/openzfs_sysctl.c \ diff --git a/darwin/Platform.c b/darwin/Platform.c index 332752b26..27bce5510 100644 --- a/darwin/Platform.c +++ b/darwin/Platform.c @@ -24,6 +24,7 @@ in the source distribution for its full text. #include "CRT.h" #include "DateMeter.h" #include "DateTimeMeter.h" +#include "FileDescriptorMeter.h" #include "HostnameMeter.h" #include "LoadAverageMeter.h" #include "Macros.h" @@ -36,6 +37,7 @@ in the source distribution for its full text. #include "UptimeMeter.h" #include "darwin/DarwinProcessList.h" #include "darwin/PlatformHelpers.h" +#include "generic/fdstat_sysctl.h" #include "zfs/ZfsArcMeter.h" #include "zfs/ZfsCompressedArcMeter.h" @@ -126,6 +128,7 @@ const MeterClass* const Platform_meterTypes[] = { &RightCPUs8Meter_class, &ZfsArcMeter_class, &ZfsCompressedArcMeter_class, + &FileDescriptorMeter_class, &BlankMeter_class, NULL }; @@ -349,6 +352,10 @@ FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) { return NULL; } +void Platform_getFileDescriptors(double* used, double* max) { + Generic_getFileDescriptors_sysctl(used, max); +} + bool Platform_getDiskIO(DiskIOData* data) { // TODO (void)data; diff --git a/darwin/Platform.h b/darwin/Platform.h index 66362072e..5cd672979 100644 --- a/darwin/Platform.h +++ b/darwin/Platform.h @@ -70,6 +70,8 @@ char* Platform_getProcessEnv(pid_t pid); FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid); +void Platform_getFileDescriptors(double* used, double* max); + bool Platform_getDiskIO(DiskIOData* data); bool Platform_getNetworkIO(NetworkIOData* data); From 27c9e79ab1b14a61f99ee912ef58d469ee4f82b9 Mon Sep 17 00:00:00 2001 From: Benny Baumann Date: Tue, 14 Feb 2023 10:15:21 +0100 Subject: [PATCH 056/106] Implement File Descriptor Meter stub for Solaris --- solaris/Platform.c | 5 +++++ solaris/Platform.h | 2 ++ 2 files changed, 7 insertions(+) diff --git a/solaris/Platform.c b/solaris/Platform.c index 96f35263d..4cadf1a00 100644 --- a/solaris/Platform.c +++ b/solaris/Platform.c @@ -313,6 +313,11 @@ FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) { return NULL; } +void Platform_getFileDescriptors(double* used, double* max) { + *used = NAN; + *max = NAN; +} + bool Platform_getDiskIO(DiskIOData* data) { // TODO (void)data; diff --git a/solaris/Platform.h b/solaris/Platform.h index 14431e356..3dc6e3b59 100644 --- a/solaris/Platform.h +++ b/solaris/Platform.h @@ -88,6 +88,8 @@ char* Platform_getProcessEnv(pid_t pid); FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid); +void Platform_getFileDescriptors(double* used, double* max); + bool Platform_getDiskIO(DiskIOData* data); bool Platform_getNetworkIO(NetworkIOData* data); From 89872e33d80f953e8895e0d0d91d8da8b7562438 Mon Sep 17 00:00:00 2001 From: Benny Baumann Date: Tue, 14 Feb 2023 10:59:04 +0100 Subject: [PATCH 057/106] Use automagic scaling for bar mode of the File Descriptor meter --- FileDescriptorMeter.c | 47 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 42 insertions(+), 5 deletions(-) diff --git a/FileDescriptorMeter.c b/FileDescriptorMeter.c index 00d088c8b..e11637105 100644 --- a/FileDescriptorMeter.c +++ b/FileDescriptorMeter.c @@ -19,6 +19,8 @@ in the source distribution for its full text. #include "XUtils.h" +#define FD_EFFECTIVE_UNLIMITED(x) ((x) > (1<<30)) + static const int FileDescriptorMeter_attributes[] = { FILE_DESCRIPTOR_USED, FILE_DESCRIPTOR_MAX @@ -33,11 +35,42 @@ static void FileDescriptorMeter_updateValues(Meter* this) { /* only print bar for first value */ this->curItems = 1; - /* Use maximum value for scaling of bar mode */ - this->total = this->values[1]; + /* Use maximum value for scaling of bar mode + * + * As the plain total value can be very large compared to + * the actually used value, this is capped in the following way: + * + * 1. If the maximum value is below (or equal to) 1<<16, use it directly + * 2. If the maximum value is above, use powers of 2 starting at 1<<16 and + * double it until it's larger than 16 times the used file handles + * (capped at the maximum number of files) + * 3. If the maximum is effectively unlimited (AKA > 1<<30), + * Do the same as for 2, but cap at 1<<30. + */ + if (this->values[1] <= 1<<16) { + this->total = this->values[1]; + } else { + if (this->total < 16 * this->values[0]) { + for (this->total = 1<<16; this->total < 16 * this->values[0]; this->total *= 2) { + if (this->total >= 1<<30) { + break; + } + } + } + + if (this->total > this->values[1]) { + this->total = this->values[1]; + } + + if (this->total > 1<<30) { + this->total = 1<<30; + } + } if (isnan(this->values[0])) { xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "unknown/unknown"); + } else if (isnan(this->values[1]) || FD_EFFECTIVE_UNLIMITED(this->values[1])) { + xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%.0lf/unlimited", this->values[0]); } else { xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%.0lf/%.0lf", this->values[0], this->values[1]); } @@ -58,8 +91,12 @@ static void FileDescriptorMeter_display(const Object* cast, RichString* out) { RichString_appendnAscii(out, CRT_colors[FILE_DESCRIPTOR_USED], buffer, len); RichString_appendAscii(out, CRT_colors[METER_TEXT], " max: "); - len = xSnprintf(buffer, sizeof(buffer), "%.0lf", this->values[1]); - RichString_appendnAscii(out, CRT_colors[FILE_DESCRIPTOR_MAX], buffer, len); + if (isnan(this->values[1]) || FD_EFFECTIVE_UNLIMITED(this->values[1])) { + RichString_appendAscii(out, CRT_colors[FILE_DESCRIPTOR_MAX], "unlimited"); + } else { + len = xSnprintf(buffer, sizeof(buffer), "%.0lf", this->values[1]); + RichString_appendnAscii(out, CRT_colors[FILE_DESCRIPTOR_MAX], buffer, len); + } } const MeterClass FileDescriptorMeter_class = { @@ -71,7 +108,7 @@ const MeterClass FileDescriptorMeter_class = { .updateValues = FileDescriptorMeter_updateValues, .defaultMode = TEXT_METERMODE, .maxItems = 2, - .total = 100.0, + .total = 65536.0, .attributes = FileDescriptorMeter_attributes, .name = "FileDescriptors", .uiName = "File Descriptors", From c66f99b1ca10247f4d419f18ffcb71bfed799f28 Mon Sep 17 00:00:00 2001 From: Ximalas Date: Sat, 25 Feb 2023 13:46:13 +0100 Subject: [PATCH 058/106] Update include order for fdstat_sysctl.c Some weird systems need you to include sys/types.h before any other sys/ headers (e.g. on FreeBSD). --- generic/fdstat_sysctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generic/fdstat_sysctl.c b/generic/fdstat_sysctl.c index 9e89a0da5..49e8e362a 100644 --- a/generic/fdstat_sysctl.c +++ b/generic/fdstat_sysctl.c @@ -11,8 +11,8 @@ in the source distribution for its full text. #include #include +#include // Shitty FreeBSD upstream headers #include -#include #include "config.h" From 11318b5ef6de6b2f80186a888cd5477e0ff167bb Mon Sep 17 00:00:00 2001 From: Daniel Lange Date: Sat, 25 Feb 2023 14:12:45 +0100 Subject: [PATCH 059/106] Remove LXC special handling for the CPU count LXC shows the real host CPU ids but can be limited in configuration as to which cores are used. Still the sysfs files are visible and the CPUs (stay) marked online. We will need to parse /sys/devices/system/cpu/online to follow LXC's logic. Revert for now until we can come up with a better handling of the LXC hacks. Cf. issue #1195 Essentially reverting 33973f7e and 0d53245c (#993, #995) --- linux/LinuxProcessList.c | 30 +----------------------------- 1 file changed, 1 insertion(+), 29 deletions(-) diff --git a/linux/LinuxProcessList.c b/linux/LinuxProcessList.c index 24b7ea98f..f8d56b701 100644 --- a/linux/LinuxProcessList.c +++ b/linux/LinuxProcessList.c @@ -210,28 +210,6 @@ static void LinuxProcessList_initNetlinkSocket(LinuxProcessList* this) { #endif -static unsigned int scanAvailableCPUsFromCPUinfo(LinuxProcessList* this) { - FILE* file = fopen(PROCCPUINFOFILE, "r"); - if (file == NULL) - return this->super.existingCPUs; - - unsigned int availableCPUs = 0; - - while (!feof(file)) { - char buffer[PROC_LINE_LENGTH]; - - if (fgets(buffer, PROC_LINE_LENGTH, file) == NULL) - break; - - if (String_startsWith(buffer, "processor")) - availableCPUs++; - } - - fclose(file); - - return availableCPUs ? availableCPUs : 1; -} - static void LinuxProcessList_updateCPUcount(ProcessList* super) { /* Similar to get_nprocs_conf(3) / _SC_NPROCESSORS_CONF * https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/unix/sysv/linux/getsysstats.c;hb=HEAD @@ -306,12 +284,6 @@ static void LinuxProcessList_updateCPUcount(ProcessList* super) { if (existing < 1) return; - if (Running_containerized) { - /* LXC munges /proc/cpuinfo but not the /sys/devices/system/cpu/ files, - * so limit the visible CPUs to what the guest has been configured to see: */ - currExisting = active = scanAvailableCPUsFromCPUinfo(this); - } - #ifdef HAVE_SENSORS_SENSORS_H /* When started with offline CPUs, libsensors does not monitor those, * even when they become online. */ @@ -320,7 +292,7 @@ static void LinuxProcessList_updateCPUcount(ProcessList* super) { #endif super->activeCPUs = active; - assert(Running_containerized || (existing == currExisting)); + assert(existing == currExisting); super->existingCPUs = currExisting; } From c803ec6dae5556fa35d6bd8124aa536633887f77 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 3 Mar 2023 23:47:43 +0100 Subject: [PATCH 060/106] Improve code readability by using enum values instead of raw numbers --- dragonflybsd/Platform.c | 6 +++--- freebsd/Platform.c | 6 +++--- linux/Platform.c | 10 +++++----- netbsd/Platform.c | 8 ++++---- openbsd/Platform.c | 8 ++++---- pcp/Platform.c | 15 ++++++++------- solaris/Platform.c | 6 +++--- 7 files changed, 30 insertions(+), 29 deletions(-) diff --git a/dragonflybsd/Platform.c b/dragonflybsd/Platform.c index 277f1876f..ab21d3650 100644 --- a/dragonflybsd/Platform.c +++ b/dragonflybsd/Platform.c @@ -192,11 +192,11 @@ double Platform_setCPUValues(Meter* this, unsigned int cpu) { v[CPU_METER_KERNEL] = cpuData->systemPercent; v[CPU_METER_IRQ] = cpuData->irqPercent; this->curItems = 4; - percent = v[0] + v[1] + v[2] + v[3]; + percent = v[CPU_METER_NICE] + v[CPU_METER_NORMAL] + v[CPU_METER_KERNEL] + v[CPU_METER_IRQ]; } else { - v[2] = cpuData->systemAllPercent; + v[CPU_METER_KERNEL] = cpuData->systemAllPercent; this->curItems = 3; - percent = v[0] + v[1] + v[2]; + percent = v[CPU_METER_NICE] + v[CPU_METER_NORMAL] + v[CPU_METER_KERNEL]; } percent = isnan(percent) ? 0.0 : CLAMP(percent, 0.0, 100.0); diff --git a/freebsd/Platform.c b/freebsd/Platform.c index 061de70bc..618ed6b42 100644 --- a/freebsd/Platform.c +++ b/freebsd/Platform.c @@ -213,11 +213,11 @@ double Platform_setCPUValues(Meter* this, unsigned int cpu) { v[CPU_METER_KERNEL] = cpuData->systemPercent; v[CPU_METER_IRQ] = cpuData->irqPercent; this->curItems = 4; - percent = v[0] + v[1] + v[2] + v[3]; + percent = v[CPU_METER_NICE] + v[CPU_METER_NORMAL] + v[CPU_METER_KERNEL] + v[CPU_METER_IRQ]; } else { - v[2] = cpuData->systemAllPercent; + v[CPU_METER_NORMAL] = cpuData->systemAllPercent; this->curItems = 3; - percent = v[0] + v[1] + v[2]; + percent = v[CPU_METER_NICE] + v[CPU_METER_NORMAL] + v[CPU_METER_KERNEL]; } percent = CLAMP(percent, 0.0, 100.0); diff --git a/linux/Platform.c b/linux/Platform.c index 005d8316b..93be689d0 100644 --- a/linux/Platform.c +++ b/linux/Platform.c @@ -325,15 +325,15 @@ double Platform_setCPUValues(Meter* this, unsigned int cpu) { v[CPU_METER_IOWAIT] = cpuData->ioWaitPeriod / total * 100.0; this->curItems = 8; if (this->pl->settings->accountGuestInCPUMeter) { - percent = v[0] + v[1] + v[2] + v[3] + v[4] + v[5] + v[6]; + percent = v[CPU_METER_NICE] + v[CPU_METER_NORMAL] + v[CPU_METER_KERNEL] + v[CPU_METER_IRQ] + v[CPU_METER_SOFTIRQ] + v[CPU_METER_STEAL] + v[CPU_METER_GUEST]; } else { - percent = v[0] + v[1] + v[2] + v[3] + v[4]; + percent = v[CPU_METER_NICE] + v[CPU_METER_NORMAL] + v[CPU_METER_KERNEL] + v[CPU_METER_IRQ] + v[CPU_METER_SOFTIRQ]; } } else { - v[2] = cpuData->systemAllPeriod / total * 100.0; - v[3] = (cpuData->stealPeriod + cpuData->guestPeriod) / total * 100.0; + v[CPU_METER_KERNEL] = cpuData->systemAllPeriod / total * 100.0; + v[CPU_METER_IRQ] = (cpuData->stealPeriod + cpuData->guestPeriod) / total * 100.0; this->curItems = 4; - percent = v[0] + v[1] + v[2] + v[3]; + percent = v[CPU_METER_NICE] + v[CPU_METER_NORMAL] + v[CPU_METER_KERNEL] + v[CPU_METER_IRQ]; } percent = CLAMP(percent, 0.0, 100.0); if (isnan(percent)) { diff --git a/netbsd/Platform.c b/netbsd/Platform.c index c5c42d0ab..9b07d8f05 100644 --- a/netbsd/Platform.c +++ b/netbsd/Platform.c @@ -254,11 +254,11 @@ double Platform_setCPUValues(Meter* this, int cpu) { v[CPU_METER_IOWAIT] = 0.0; v[CPU_METER_FREQUENCY] = NAN; this->curItems = 8; - totalPercent = v[0] + v[1] + v[2] + v[3]; + totalPercent = v[CPU_METER_NICE] + v[CPU_METER_NORMAL] + v[CPU_METER_KERNEL] + v[CPU_METER_IRQ]; } else { - v[2] = cpuData->sysAllPeriod / total * 100.0; - v[3] = 0.0; // No steal nor guest on NetBSD - totalPercent = v[0] + v[1] + v[2]; + v[CPU_METER_KERNEL] = cpuData->sysAllPeriod / total * 100.0; + v[CPU_METER_IRQ] = 0.0; // No steal nor guest on NetBSD + totalPercent = v[CPU_METER_NICE] + v[CPU_METER_NORMAL] + v[CPU_METER_KERNEL]; this->curItems = 4; } diff --git a/openbsd/Platform.c b/openbsd/Platform.c index 71419f8eb..615397e2b 100644 --- a/openbsd/Platform.c +++ b/openbsd/Platform.c @@ -205,11 +205,11 @@ double Platform_setCPUValues(Meter* this, unsigned int cpu) { v[CPU_METER_IOWAIT] = 0.0; v[CPU_METER_FREQUENCY] = NAN; this->curItems = 8; - totalPercent = v[0] + v[1] + v[2] + v[3]; + totalPercent = v[CPU_METER_NICE] + v[CPU_METER_NORMAL] + v[CPU_METER_KERNEL] + v[CPU_METER_IRQ]; } else { - v[2] = cpuData->sysAllPeriod / total * 100.0; - v[3] = 0.0; // No steal nor guest on OpenBSD - totalPercent = v[0] + v[1] + v[2]; + v[CPU_METER_KERNEL] = cpuData->sysAllPeriod / total * 100.0; + v[CPU_METER_IRQ] = 0.0; // No steal nor guest on OpenBSD + totalPercent = v[CPU_METER_NICE] + v[CPU_METER_NORMAL] + v[CPU_METER_KERNEL]; this->curItems = 4; } diff --git a/pcp/Platform.c b/pcp/Platform.c index a76c0288a..7319fcb00 100644 --- a/pcp/Platform.c +++ b/pcp/Platform.c @@ -501,16 +501,17 @@ static double Platform_setOneCPUValues(Meter* this, pmAtomValue* values) { v[CPU_METER_GUEST] = values[CPU_GUEST_PERIOD].ull / total * 100.0; v[CPU_METER_IOWAIT] = values[CPU_IOWAIT_PERIOD].ull / total * 100.0; this->curItems = 8; - if (this->pl->settings->accountGuestInCPUMeter) - percent = v[0] + v[1] + v[2] + v[3] + v[4] + v[5] + v[6]; - else - percent = v[0] + v[1] + v[2] + v[3] + v[4]; + if (this->pl->settings->accountGuestInCPUMeter) { + percent = v[CPU_METER_NICE] + v[CPU_METER_NORMAL] + v[CPU_METER_KERNEL] + v[CPU_METER_IRQ] + v[CPU_METER_SOFTIRQ] + v[CPU_METER_STEAL] + v[CPU_METER_GUEST]; + } else { + percent = v[CPU_METER_NICE] + v[CPU_METER_NORMAL] + v[CPU_METER_KERNEL] + v[CPU_METER_IRQ] + v[CPU_METER_SOFTIRQ]; + } } else { - v[2] = values[CPU_SYSTEM_ALL_PERIOD].ull / total * 100.0; + v[CPU_METER_KERNEL] = values[CPU_SYSTEM_ALL_PERIOD].ull / total * 100.0; value = values[CPU_STEAL_PERIOD].ull + values[CPU_GUEST_PERIOD].ull; - v[3] = value / total * 100.0; + v[CPU_METER_IRQ] = value / total * 100.0; this->curItems = 4; - percent = v[0] + v[1] + v[2] + v[3]; + percent = v[CPU_METER_NICE] + v[CPU_METER_NORMAL] + v[CPU_METER_KERNEL] + v[CPU_METER_IRQ]; } percent = CLAMP(percent, 0.0, 100.0); if (isnan(percent)) diff --git a/solaris/Platform.c b/solaris/Platform.c index 4cadf1a00..f7ab65585 100644 --- a/solaris/Platform.c +++ b/solaris/Platform.c @@ -219,11 +219,11 @@ double Platform_setCPUValues(Meter* this, unsigned int cpu) { v[CPU_METER_KERNEL] = cpuData->systemPercent; v[CPU_METER_IRQ] = cpuData->irqPercent; this->curItems = 4; - percent = v[0] + v[1] + v[2] + v[3]; + percent = v[CPU_METER_NICE] + v[CPU_METER_NORMAL] + v[CPU_METER_KERNEL] + v[CPU_METER_IRQ]; } else { - v[2] = cpuData->systemAllPercent; + v[CPU_METER_KERNEL] = cpuData->systemAllPercent; this->curItems = 3; - percent = v[0] + v[1] + v[2]; + percent = v[CPU_METER_NICE] + v[CPU_METER_NORMAL] + v[CPU_METER_KERNEL]; } percent = isnan(percent) ? 0.0 : CLAMP(percent, 0.0, 100.0); From e207c8aebdcdb88bc8ab838e2ac3dd1774d6a618 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 4 Mar 2023 12:09:24 +0100 Subject: [PATCH 061/106] Improve CPU computation code --- linux/Platform.c | 5 ++--- netbsd/Platform.c | 4 +--- openbsd/Platform.c | 3 +-- pcp/Platform.c | 5 ++--- 4 files changed, 6 insertions(+), 11 deletions(-) diff --git a/linux/Platform.c b/linux/Platform.c index 93be689d0..92da08581 100644 --- a/linux/Platform.c +++ b/linux/Platform.c @@ -324,10 +324,9 @@ double Platform_setCPUValues(Meter* this, unsigned int cpu) { v[CPU_METER_GUEST] = cpuData->guestPeriod / total * 100.0; v[CPU_METER_IOWAIT] = cpuData->ioWaitPeriod / total * 100.0; this->curItems = 8; + percent = v[CPU_METER_NICE] + v[CPU_METER_NORMAL] + v[CPU_METER_KERNEL] + v[CPU_METER_IRQ] + v[CPU_METER_SOFTIRQ]; if (this->pl->settings->accountGuestInCPUMeter) { - percent = v[CPU_METER_NICE] + v[CPU_METER_NORMAL] + v[CPU_METER_KERNEL] + v[CPU_METER_IRQ] + v[CPU_METER_SOFTIRQ] + v[CPU_METER_STEAL] + v[CPU_METER_GUEST]; - } else { - percent = v[CPU_METER_NICE] + v[CPU_METER_NORMAL] + v[CPU_METER_KERNEL] + v[CPU_METER_IRQ] + v[CPU_METER_SOFTIRQ]; + percent += v[CPU_METER_STEAL] + v[CPU_METER_GUEST]; } } else { v[CPU_METER_KERNEL] = cpuData->systemAllPeriod / total * 100.0; diff --git a/netbsd/Platform.c b/netbsd/Platform.c index 9b07d8f05..a79f9c0b7 100644 --- a/netbsd/Platform.c +++ b/netbsd/Platform.c @@ -254,14 +254,12 @@ double Platform_setCPUValues(Meter* this, int cpu) { v[CPU_METER_IOWAIT] = 0.0; v[CPU_METER_FREQUENCY] = NAN; this->curItems = 8; - totalPercent = v[CPU_METER_NICE] + v[CPU_METER_NORMAL] + v[CPU_METER_KERNEL] + v[CPU_METER_IRQ]; } else { v[CPU_METER_KERNEL] = cpuData->sysAllPeriod / total * 100.0; v[CPU_METER_IRQ] = 0.0; // No steal nor guest on NetBSD - totalPercent = v[CPU_METER_NICE] + v[CPU_METER_NORMAL] + v[CPU_METER_KERNEL]; this->curItems = 4; } - + totalPercent = v[CPU_METER_NICE] + v[CPU_METER_NORMAL] + v[CPU_METER_KERNEL] + v[CPU_METER_IRQ]; totalPercent = CLAMP(totalPercent, 0.0, 100.0); v[CPU_METER_FREQUENCY] = cpuData->frequency; diff --git a/openbsd/Platform.c b/openbsd/Platform.c index 615397e2b..e49a0f817 100644 --- a/openbsd/Platform.c +++ b/openbsd/Platform.c @@ -205,13 +205,12 @@ double Platform_setCPUValues(Meter* this, unsigned int cpu) { v[CPU_METER_IOWAIT] = 0.0; v[CPU_METER_FREQUENCY] = NAN; this->curItems = 8; - totalPercent = v[CPU_METER_NICE] + v[CPU_METER_NORMAL] + v[CPU_METER_KERNEL] + v[CPU_METER_IRQ]; } else { v[CPU_METER_KERNEL] = cpuData->sysAllPeriod / total * 100.0; v[CPU_METER_IRQ] = 0.0; // No steal nor guest on OpenBSD - totalPercent = v[CPU_METER_NICE] + v[CPU_METER_NORMAL] + v[CPU_METER_KERNEL]; this->curItems = 4; } + totalPercent = v[CPU_METER_NICE] + v[CPU_METER_NORMAL] + v[CPU_METER_KERNEL] + v[CPU_METER_IRQ]; totalPercent = CLAMP(totalPercent, 0.0, 100.0); diff --git a/pcp/Platform.c b/pcp/Platform.c index 7319fcb00..22963dda3 100644 --- a/pcp/Platform.c +++ b/pcp/Platform.c @@ -501,10 +501,9 @@ static double Platform_setOneCPUValues(Meter* this, pmAtomValue* values) { v[CPU_METER_GUEST] = values[CPU_GUEST_PERIOD].ull / total * 100.0; v[CPU_METER_IOWAIT] = values[CPU_IOWAIT_PERIOD].ull / total * 100.0; this->curItems = 8; + percent = v[CPU_METER_NICE] + v[CPU_METER_NORMAL] + v[CPU_METER_KERNEL] + v[CPU_METER_IRQ] + v[CPU_METER_SOFTIRQ]; if (this->pl->settings->accountGuestInCPUMeter) { - percent = v[CPU_METER_NICE] + v[CPU_METER_NORMAL] + v[CPU_METER_KERNEL] + v[CPU_METER_IRQ] + v[CPU_METER_SOFTIRQ] + v[CPU_METER_STEAL] + v[CPU_METER_GUEST]; - } else { - percent = v[CPU_METER_NICE] + v[CPU_METER_NORMAL] + v[CPU_METER_KERNEL] + v[CPU_METER_IRQ] + v[CPU_METER_SOFTIRQ]; + percent += v[CPU_METER_STEAL] + v[CPU_METER_GUEST]; } } else { v[CPU_METER_KERNEL] = values[CPU_SYSTEM_ALL_PERIOD].ull / total * 100.0; From a5c46500f39bab23b513567bd1a7af197ea707c5 Mon Sep 17 00:00:00 2001 From: Daniel Lange Date: Wed, 29 Mar 2023 10:43:58 +0200 Subject: [PATCH 062/106] Increase Search and Filter max string length to 128 As per discussion in https://github.com/htop-dev/htop/issues/1201#issuecomment-1486340598 Closes #1201, closes #1202 --- IncSet.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/IncSet.h b/IncSet.h index 15b5d5d22..a84407e50 100644 --- a/IncSet.h +++ b/IncSet.h @@ -15,7 +15,7 @@ in the source distribution for its full text. #include "Vector.h" -#define INCMODE_MAX 40 +#define INCMODE_MAX 128 typedef enum { INC_SEARCH = 0, From f66f04e6e0e6350ff8ea8242031ad43ce06a0a2b Mon Sep 17 00:00:00 2001 From: Stefan Date: Thu, 30 Mar 2023 11:25:46 +0200 Subject: [PATCH 063/106] Minor typo --- Process.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Process.c b/Process.c index 5a179b424..c92d01b99 100644 --- a/Process.c +++ b/Process.c @@ -434,7 +434,7 @@ void Process_makeCommandStr(Process* this) { mc->lastUpdate = settingsStamp; - /* The field separtor "│" has been chosen such that it will not match any + /* The field separator "│" has been chosen such that it will not match any * valid string used for searching or filtering */ const char* SEPARATOR = CRT_treeStr[TREE_STR_VERT]; const int SEPARATOR_LEN = strlen(SEPARATOR); From b618c379e13e7a2bb688cd54a64d199a267c0438 Mon Sep 17 00:00:00 2001 From: Kirill Tkhai Date: Fri, 31 Mar 2023 00:10:40 +0300 Subject: [PATCH 064/106] This patch is for "Hide userland process threads mode is off" case. Below is a small example of two threads program, whose main thread statistics is incorrect in htop. Despite main thread is permanently sleeping, its CPU load is 100% (must be 0%). CPU load of secondary thread is correct (100%). void *thread_func(void *data) { while(1) { } return NULL; } int main() { pthread_t thread; pthread_create(&thread, NULL, thread_func, NULL); pthread_join(thread, NULL); } The reason is in there is a difference in behavior of some files in /proc/pid and /proc/pid/task/pid directories. For example, /proc/pid/stat shows agregated user and sys times for all threads of a process. For the details please see do_task_stat() implementation and this function behavior difference in dependence of last argument: https://elixir.bootlin.com/linux/v6.2.8/source/fs/proc/array.c#L652 So, the problem occurs because of user and sys times of main thread are polluted by secondary thread statistics. This patch fixes the problem by reading correct stat from /proc/pid/task/pid/stat for main thread. Looking at other files from /proc/pid directory I found /proc/pid/io file with the same problem: https://elixir.bootlin.com/linux/v6.2.8/source/fs/proc/base.c#L3029 This problem is also fixed in this patch by reading correct data from /proc/pid/task/pid/io file for main thread. /proc/pid directory files are declared in tgid_base_stuff, while /proc/pid/task/tid directory files are declared in tid_base_stuff in kernel's fs/proc/base.c file: https://elixir.bootlin.com/linux/v6.2.8/source/fs/proc/base.c#L3238 I checked the difference between rest of declarations and it looks like there is no more problems of such type affecting htop. --- linux/LinuxProcessList.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/linux/LinuxProcessList.c b/linux/LinuxProcessList.c index f8d56b701..b94c24a16 100644 --- a/linux/LinuxProcessList.c +++ b/linux/LinuxProcessList.c @@ -14,6 +14,7 @@ in the source distribution for its full text. #include #include #include +#include #include #include #include @@ -382,11 +383,15 @@ static inline ProcessState LinuxProcessList_getProcessState(char state) { } } -static bool LinuxProcessList_readStatFile(Process* process, openat_arg_t procFd, char* command, size_t commLen) { - LinuxProcess* lp = (LinuxProcess*) process; +static bool LinuxProcessList_readStatFile(LinuxProcess* lp, openat_arg_t procFd, bool scanMainThread, char* command, size_t commLen) { + Process* process = &lp->super; char buf[MAX_READ + 1]; - ssize_t r = xReadfileat(procFd, "stat", buf, sizeof(buf)); + char path[22] = "stat"; + if (scanMainThread) { + xSnprintf(path, sizeof(path), "task/%"PRIi32"/stat", (int32_t)process->pid); + } + ssize_t r = xReadfileat(procFd, path, buf, sizeof(buf)); if (r < 0) return false; @@ -607,9 +612,14 @@ static bool LinuxProcessList_updateUser(ProcessList* processList, Process* proce return true; } -static void LinuxProcessList_readIoFile(LinuxProcess* process, openat_arg_t procFd, unsigned long long realtimeMs) { +static void LinuxProcessList_readIoFile(LinuxProcess* process, openat_arg_t procFd, bool scanMainThread, unsigned long long realtimeMs) { + Process *proc = &process->super; + char path[20] = "io"; char buffer[1024]; - ssize_t r = xReadfileat(procFd, "io", buffer, sizeof(buffer)); + if (scanMainThread) { + xSnprintf(path, sizeof(path), "task/%"PRIi32"/io", (int32_t)proc->pid); + } + ssize_t r = xReadfileat(procFd, path, buffer, sizeof(buffer)); if (r < 0) { process->io_rate_read_bps = NAN; process->io_rate_write_bps = NAN; @@ -1531,8 +1541,9 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ continue; } + bool scanMainThread = !hideUserlandThreads && !Process_isKernelThread(proc) && !parent; if (ss->flags & PROCESS_FLAG_IO) - LinuxProcessList_readIoFile(lp, procFd, pl->realtimeMs); + LinuxProcessList_readIoFile(lp, procFd, scanMainThread, pl->realtimeMs); if (!LinuxProcessList_readStatmFile(lp, procFd)) goto errorReadingProcess; @@ -1580,7 +1591,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ char statCommand[MAX_NAME + 1]; unsigned long long int lasttimes = (lp->utime + lp->stime); unsigned long int tty_nr = proc->tty_nr; - if (!LinuxProcessList_readStatFile(proc, procFd, statCommand, sizeof(statCommand))) + if (!LinuxProcessList_readStatFile(lp, procFd, scanMainThread, statCommand, sizeof(statCommand))) goto errorReadingProcess; if (lp->flags & PF_KTHREAD) { From ab49f3f18042f894b52903cd80a102991ed8167b Mon Sep 17 00:00:00 2001 From: Kirill Tkhai Date: Fri, 31 Mar 2023 22:15:08 +0300 Subject: [PATCH 065/106] Naming convention: LinuxProcess *lp and Process *process. --- linux/LinuxProcessList.c | 52 ++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/linux/LinuxProcessList.c b/linux/LinuxProcessList.c index b94c24a16..0dbabe5ba 100644 --- a/linux/LinuxProcessList.c +++ b/linux/LinuxProcessList.c @@ -612,31 +612,31 @@ static bool LinuxProcessList_updateUser(ProcessList* processList, Process* proce return true; } -static void LinuxProcessList_readIoFile(LinuxProcess* process, openat_arg_t procFd, bool scanMainThread, unsigned long long realtimeMs) { - Process *proc = &process->super; +static void LinuxProcessList_readIoFile(LinuxProcess* lp, openat_arg_t procFd, bool scanMainThread, unsigned long long realtimeMs) { + Process *process = &lp->super; char path[20] = "io"; char buffer[1024]; if (scanMainThread) { - xSnprintf(path, sizeof(path), "task/%"PRIi32"/io", (int32_t)proc->pid); + xSnprintf(path, sizeof(path), "task/%"PRIi32"/io", (int32_t)process->pid); } ssize_t r = xReadfileat(procFd, path, buffer, sizeof(buffer)); if (r < 0) { - process->io_rate_read_bps = NAN; - process->io_rate_write_bps = NAN; - process->io_rchar = ULLONG_MAX; - process->io_wchar = ULLONG_MAX; - process->io_syscr = ULLONG_MAX; - process->io_syscw = ULLONG_MAX; - process->io_read_bytes = ULLONG_MAX; - process->io_write_bytes = ULLONG_MAX; - process->io_cancelled_write_bytes = ULLONG_MAX; - process->io_last_scan_time_ms = realtimeMs; + lp->io_rate_read_bps = NAN; + lp->io_rate_write_bps = NAN; + lp->io_rchar = ULLONG_MAX; + lp->io_wchar = ULLONG_MAX; + lp->io_syscr = ULLONG_MAX; + lp->io_syscw = ULLONG_MAX; + lp->io_read_bytes = ULLONG_MAX; + lp->io_write_bytes = ULLONG_MAX; + lp->io_cancelled_write_bytes = ULLONG_MAX; + lp->io_last_scan_time_ms = realtimeMs; return; } - unsigned long long last_read = process->io_read_bytes; - unsigned long long last_write = process->io_write_bytes; - unsigned long long time_delta = realtimeMs > process->io_last_scan_time_ms ? realtimeMs - process->io_last_scan_time_ms : 0; + unsigned long long last_read = lp->io_read_bytes; + unsigned long long last_write = lp->io_write_bytes; + unsigned long long time_delta = realtimeMs > lp->io_last_scan_time_ms ? realtimeMs - lp->io_last_scan_time_ms : 0; char* buf = buffer; const char* line; @@ -644,35 +644,35 @@ static void LinuxProcessList_readIoFile(LinuxProcess* process, openat_arg_t proc switch (line[0]) { case 'r': if (line[1] == 'c' && String_startsWith(line + 2, "har: ")) { - process->io_rchar = strtoull(line + 7, NULL, 10); + lp->io_rchar = strtoull(line + 7, NULL, 10); } else if (String_startsWith(line + 1, "ead_bytes: ")) { - process->io_read_bytes = strtoull(line + 12, NULL, 10); - process->io_rate_read_bps = time_delta ? (process->io_read_bytes - last_read) * /*ms to s*/1000. / time_delta : NAN; + lp->io_read_bytes = strtoull(line + 12, NULL, 10); + lp->io_rate_read_bps = time_delta ? (lp->io_read_bytes - last_read) * /*ms to s*/1000. / time_delta : NAN; } break; case 'w': if (line[1] == 'c' && String_startsWith(line + 2, "har: ")) { - process->io_wchar = strtoull(line + 7, NULL, 10); + lp->io_wchar = strtoull(line + 7, NULL, 10); } else if (String_startsWith(line + 1, "rite_bytes: ")) { - process->io_write_bytes = strtoull(line + 13, NULL, 10); - process->io_rate_write_bps = time_delta ? (process->io_write_bytes - last_write) * /*ms to s*/1000. / time_delta : NAN; + lp->io_write_bytes = strtoull(line + 13, NULL, 10); + lp->io_rate_write_bps = time_delta ? (lp->io_write_bytes - last_write) * /*ms to s*/1000. / time_delta : NAN; } break; case 's': if (line[4] == 'r' && String_startsWith(line + 1, "yscr: ")) { - process->io_syscr = strtoull(line + 7, NULL, 10); + lp->io_syscr = strtoull(line + 7, NULL, 10); } else if (String_startsWith(line + 1, "yscw: ")) { - process->io_syscw = strtoull(line + 7, NULL, 10); + lp->io_syscw = strtoull(line + 7, NULL, 10); } break; case 'c': if (String_startsWith(line + 1, "ancelled_write_bytes: ")) { - process->io_cancelled_write_bytes = strtoull(line + 23, NULL, 10); + lp->io_cancelled_write_bytes = strtoull(line + 23, NULL, 10); } } } - process->io_last_scan_time_ms = realtimeMs; + lp->io_last_scan_time_ms = realtimeMs; } typedef struct LibraryData_ { From 61e7cb17a2fb0e28ae95c440544c1529d363f8bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= Date: Sat, 1 Apr 2023 15:19:20 +0200 Subject: [PATCH 066/106] Use shared real memory on FreeBSD Cf. https://github.com/htop-dev/htop/issues/1193#issuecomment-1435673333 for more details. Fixes: #1193 Thanks: @er-azh --- freebsd/FreeBSDProcessList.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freebsd/FreeBSDProcessList.c b/freebsd/FreeBSDProcessList.c index 98447df9a..331aac617 100644 --- a/freebsd/FreeBSDProcessList.c +++ b/freebsd/FreeBSDProcessList.c @@ -367,7 +367,7 @@ static inline void FreeBSDProcessList_scanMemoryInfo(ProcessList* pl) { len = sizeof(vmtotal); sysctl(MIB_vm_vmtotal, 2, &(vmtotal), &len, NULL, 0); - pl->sharedMem = vmtotal.t_vmshr * pageSizeKb; + pl->sharedMem = vmtotal.t_rmshr * pageSizeKb; pl->usedMem = fpl->memActive + fpl->memWire; From c707b0eb52c95930496325cedd23a4f14f47e65b Mon Sep 17 00:00:00 2001 From: Lukas Beckmann Date: Sat, 18 Feb 2023 21:13:02 +0100 Subject: [PATCH 067/106] fix some assertions in Vector_isConsistent and Vector_splice. --- Vector.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/Vector.c b/Vector.c index eddbc9a67..a4dc137fc 100644 --- a/Vector.c +++ b/Vector.c @@ -62,14 +62,6 @@ static bool Vector_isConsistent(const Vector* this) { assert(this->items <= this->arraySize); assert(!Vector_isDirty(this)); - if (this->owner) { - for (int i = 0; i < this->items; i++) { - if (!this->array[i]) { - return false; - } - } - } - return true; } @@ -396,7 +388,7 @@ int Vector_indexOf(const Vector* this, const void* search_, Object_Compare compa void Vector_splice(Vector* this, Vector* from) { assert(Vector_isConsistent(this)); assert(Vector_isConsistent(from)); - assert(!(this->owner && from->owner)); + assert(!this->owner); int olditems = this->items; this->items += from->items; From 45b334c851f8dc6f34e64caebfe91c2983f91506 Mon Sep 17 00:00:00 2001 From: Lukas Beckmann Date: Tue, 21 Feb 2023 19:08:29 +0100 Subject: [PATCH 068/106] resize vector in Vector_set and Vector_splice. --- Vector.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/Vector.c b/Vector.c index a4dc137fc..d0a9b6544 100644 --- a/Vector.c +++ b/Vector.c @@ -192,12 +192,12 @@ void Vector_insertionSort(Vector* this) { assert(Vector_isConsistent(this)); } -static void Vector_checkArraySize(Vector* this) { - assert(Vector_isConsistent(this)); - if (this->items >= this->arraySize) { - //int i; - //i = this->arraySize; - this->arraySize = this->items + this->growthRate; +static void Vector_resizeIfNecessary(Vector* this, int newSize) { + assert(newSize >= 0); + if (newSize > this->arraySize) { + assert(Vector_isConsistent(this)); + int oldSize = this->arraySize; + this->arraySize = newSize + this->growthRate; this->array = (Object**) xRealloc(this->array, sizeof(Object*) * this->arraySize); //for (; i < this->arraySize; i++) // this->array[i] = NULL; @@ -215,7 +215,7 @@ void Vector_insert(Vector* this, int idx, void* data_) { idx = this->items; } - Vector_checkArraySize(this); + Vector_resizeIfNecessary(this, this->items + 1); //assert(this->array[this->items] == NULL); if (idx < this->items) { memmove(&this->array[idx + 1], &this->array[idx], (this->items - idx) * sizeof(this->array[0])); @@ -331,14 +331,15 @@ void Vector_set(Vector* this, int idx, void* data_) { assert(Object_isA(data, this->type)); assert(Vector_isConsistent(this)); - Vector_checkArraySize(this); + Vector_resizeIfNecessary(this, idx + 1); if (idx >= this->items) { this->items = idx + 1; } else { if (this->owner) { Object* removed = this->array[idx]; - assert (removed != NULL); - Object_delete(removed); + if (removed != NULL) { + Object_delete(removed); + } } } this->array[idx] = data; @@ -391,8 +392,8 @@ void Vector_splice(Vector* this, Vector* from) { assert(!this->owner); int olditems = this->items; + Vector_resizeIfNecessary(this, this->items + from->items); this->items += from->items; - Vector_checkArraySize(this); for (int j = 0; j < from->items; j++) { this->array[olditems + j] = from->array[j]; } From 79364ac20cd8fc5889f3dfe08b961249d99facaa Mon Sep 17 00:00:00 2001 From: Lukas Beckmann Date: Tue, 21 Feb 2023 19:09:11 +0100 Subject: [PATCH 069/106] set vector slots not containing entries to NULL. --- Vector.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Vector.c b/Vector.c index d0a9b6544..47a772cee 100644 --- a/Vector.c +++ b/Vector.c @@ -95,13 +95,13 @@ void Vector_prune(Vector* this) { for (int i = 0; i < this->items; i++) { if (this->array[i]) { Object_delete(this->array[i]); - this->array[i] = NULL; } } } this->items = 0; this->dirty_index = -1; this->dirty_count = 0; + memset(this->array, '\0', this->arraySize * sizeof(Object *)); } //static int comparisons = 0; @@ -198,9 +198,7 @@ static void Vector_resizeIfNecessary(Vector* this, int newSize) { assert(Vector_isConsistent(this)); int oldSize = this->arraySize; this->arraySize = newSize + this->growthRate; - this->array = (Object**) xRealloc(this->array, sizeof(Object*) * this->arraySize); - //for (; i < this->arraySize; i++) - // this->array[i] = NULL; + this->array = (Object **)xReallocArrayZero(this->array, oldSize, this->arraySize, sizeof(Object*)); } assert(Vector_isConsistent(this)); } @@ -234,7 +232,7 @@ Object* Vector_take(Vector* this, int idx) { if (idx < this->items) { memmove(&this->array[idx], &this->array[idx + 1], (this->items - idx) * sizeof(this->array[0])); } - //this->array[this->items] = NULL; + this->array[this->items] = NULL; assert(Vector_isConsistent(this)); return removed; } @@ -282,16 +280,19 @@ void Vector_compact(Vector* this) { int idx = this->dirty_index; - /* one deletion: use memmove, which should be faster */ + // one deletion: use memmove, which should be faster if (this->dirty_count == 1) { memmove(&this->array[idx], &this->array[idx + 1], (this->items - idx - 1) * sizeof(this->array[0])); + this->array[this->items - 1] = NULL; } else { - /* multiple deletions */ + // multiple deletions for (int i = idx + 1; i < size; i++) { if (this->array[i]) { this->array[idx++] = this->array[i]; } } + // idx is now at the end of the vector and on the first index which should be set to NULL + memset(&this->array[idx], '\0', (size - idx) * sizeof(this->array[0])); } this->items -= this->dirty_count; From c8a61850dd31ab64eb8ad83929ce86d2adbeb96c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= Date: Wed, 5 Apr 2023 01:21:14 +0200 Subject: [PATCH 070/106] configure: restore hwloc support If the pkg-config check for hwloc succeeds, actually define HAVE_LIBHWLOC to enable the conditional code. Fixes: 4ccad460 ("configure.ac: fix static build with hwloc") --- configure.ac | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 015f90461..a8b75dcde 100644 --- a/configure.ac +++ b/configure.ac @@ -479,7 +479,9 @@ case "$enable_hwloc" in m4_ifdef([PKG_PROG_PKG_CONFIG], [ PKG_PROG_PKG_CONFIG() PKG_CHECK_MODULES(HWLOC, hwloc, [ - CFLAGS="$CFLAGS $HWLOC_CFLAGS" LIBS="$LIBS $HWLOC_LIBS" + CFLAGS="$CFLAGS $HWLOC_CFLAGS" + LIBS="$LIBS $HWLOC_LIBS" + AC_DEFINE([HAVE_LIBHWLOC], [1], [Define to 1 if you have the `hwloc' library (-lhwloc).]) ], [ AC_CHECK_LIB([hwloc], [hwloc_get_proc_cpubind], [], [AC_MSG_ERROR([can not find required library libhwloc])]) AC_CHECK_HEADERS([hwloc.h], [], [AC_MSG_ERROR([can not find require header file hwloc.h])]) From e7f447b6a3473a572d3e6c191128f1796477860d Mon Sep 17 00:00:00 2001 From: Nathan Scott Date: Tue, 4 Apr 2023 16:24:37 +1000 Subject: [PATCH 071/106] Refactor and consolidate dynamic meters/columns pointers This removes the duplication of dynamic meter/column hashtable pointers that has come in between the Settings and ProcessList structures - only one copy of these is needed. With the future planned dynamic screens feature adding another pointer, let us first clean this up before any further duplication happens. --- AvailableMetersPanel.c | 9 ++--- CommandLine.c | 6 ++-- DynamicMeter.c | 8 ++--- Header.c | 9 ++--- ProcessList.c | 47 ++++++++++++++------------ ProcessList.h | 7 ++-- Settings.c | 3 +- Settings.h | 5 +-- darwin/DarwinProcessList.c | 4 +-- darwin/DarwinProcessList.h | 2 +- dragonflybsd/DragonFlyBSDProcessList.c | 4 +-- dragonflybsd/DragonFlyBSDProcessList.h | 2 +- freebsd/FreeBSDProcessList.c | 4 +-- freebsd/FreeBSDProcessList.h | 2 +- linux/LinuxProcessList.c | 4 +-- linux/LinuxProcessList.h | 2 +- netbsd/NetBSDProcessList.c | 4 +-- netbsd/NetBSDProcessList.h | 2 +- openbsd/OpenBSDProcessList.c | 4 +-- openbsd/OpenBSDProcessList.h | 2 +- pcp/PCPDynamicColumn.c | 2 +- pcp/PCPProcessList.c | 4 +-- pcp/PCPProcessList.h | 2 +- solaris/SolarisProcessList.c | 4 +-- solaris/SolarisProcessList.h | 2 +- unsupported/UnsupportedProcessList.c | 4 +-- unsupported/UnsupportedProcessList.h | 2 +- 27 files changed, 78 insertions(+), 72 deletions(-) diff --git a/AvailableMetersPanel.c b/AvailableMetersPanel.c index c7ab89bea..aa6d7795a 100644 --- a/AvailableMetersPanel.c +++ b/AvailableMetersPanel.c @@ -128,10 +128,11 @@ static void AvailableMetersPanel_addDynamicMeter(ATTR_UNUSED ht_key_t key, void* } // Handle (&DynamicMeter_class) entries in the AvailableMetersPanel -static void AvailableMetersPanel_addDynamicMeters(Panel* super, const ProcessList* pl, unsigned int offset) { +static void AvailableMetersPanel_addDynamicMeters(Panel* super, const Settings* settings, unsigned int offset) { DynamicIterator iter = { .super = super, .id = 1, .offset = offset }; - assert(pl->dynamicMeters != NULL); - Hashtable_foreach(pl->dynamicMeters, AvailableMetersPanel_addDynamicMeter, &iter); + Hashtable* dynamicMeters = settings->dynamicColumns; + assert(dynamicMeters != NULL); + Hashtable_foreach(dynamicMeters, AvailableMetersPanel_addDynamicMeter, &iter); } // Handle remaining Platform Meter entries in the AvailableMetersPanel @@ -161,7 +162,7 @@ AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* heade const MeterClass* type = Platform_meterTypes[i]; assert(type != &CPUMeter_class); if (type == &DynamicMeter_class) - AvailableMetersPanel_addDynamicMeters(super, pl, i); + AvailableMetersPanel_addDynamicMeters(super, settings, i); else AvailableMetersPanel_addPlatformMeter(super, type, i); } diff --git a/CommandLine.c b/CommandLine.c index 682e05421..833592068 100644 --- a/CommandLine.c +++ b/CommandLine.c @@ -322,14 +322,14 @@ int CommandLine_run(const char* name, int argc, char** argv) { Process_setupColumnWidths(); UsersTable* ut = UsersTable_new(); - Hashtable* dc = DynamicColumns_new(); Hashtable* dm = DynamicMeters_new(); + Hashtable* dc = DynamicColumns_new(); if (!dc) dc = Hashtable_new(0, true); - ProcessList* pl = ProcessList_new(ut, dm, dc, flags.pidMatchList, flags.userId); + ProcessList* pl = ProcessList_new(ut, flags.pidMatchList, flags.userId); - Settings* settings = Settings_new(pl->activeCPUs, dc); + Settings* settings = Settings_new(pl->activeCPUs, dm, dc); pl->settings = settings; Header* header = Header_new(pl, settings, 2); diff --git a/DynamicMeter.c b/DynamicMeter.c index a8cd76cc9..40c06bb68 100644 --- a/DynamicMeter.c +++ b/DynamicMeter.c @@ -88,16 +88,16 @@ static void DynamicMeter_display(const Object* cast, RichString* out) { } static const char* DynamicMeter_getCaption(const Meter* this) { - const ProcessList* pl = this->pl; - const DynamicMeter* meter = Hashtable_get(pl->dynamicMeters, this->param); + const Settings* settings = this->pl->settings; + const DynamicMeter* meter = Hashtable_get(settings->dynamicMeters, this->param); if (meter) return meter->caption ? meter->caption : meter->name; return this->caption; } static void DynamicMeter_getUiName(const Meter* this, char* name, size_t length) { - const ProcessList* pl = this->pl; - const DynamicMeter* meter = Hashtable_get(pl->dynamicMeters, this->param); + const Settings* settings = this->pl->settings; + const DynamicMeter* meter = Hashtable_get(settings->dynamicMeters, this->param); if (meter) { const char* uiName = meter->caption; if (uiName) { diff --git a/Header.c b/Header.c index 1953c0206..b2fc56cc9 100644 --- a/Header.c +++ b/Header.c @@ -92,7 +92,7 @@ static void Header_addMeterByName(Header* this, const char* name, MeterModeId mo if ((end = strrchr(dynamic, ')')) == NULL) return; // htoprc parse failure *end = '\0'; - if (!DynamicMeter_search(this->pl->dynamicMeters, dynamic, ¶m)) + if (!DynamicMeter_search(this->settings->dynamicMeters, dynamic, ¶m)) return; // name lookup failure } else { param = 0; @@ -130,10 +130,11 @@ void Header_populateFromSettings(Header* this) { } void Header_writeBackToSettings(const Header* this) { - Settings_setHeaderLayout(this->settings, this->headerLayout); + Settings* settings = this->settings; + Settings_setHeaderLayout(settings, this->headerLayout); Header_forEachColumn(this, col) { - MeterColumnSetting* colSettings = &this->settings->hColumns[col]; + MeterColumnSetting* colSettings = &settings->hColumns[col]; if (colSettings->names) { for (size_t j = 0; j < colSettings->len; j++) @@ -153,7 +154,7 @@ void Header_writeBackToSettings(const Header* this) { const Meter* meter = (Meter*) Vector_get(vec, i); char* name; if (meter->param && As_Meter(meter) == &DynamicMeter_class) { - const char* dynamic = DynamicMeter_lookup(this->pl->dynamicMeters, meter->param); + const char* dynamic = DynamicMeter_lookup(settings->dynamicMeters, meter->param); xAsprintf(&name, "%s(%s)", As_Meter(meter)->name, dynamic); } else if (meter->param && As_Meter(meter) == &CPUMeter_class) { xAsprintf(&name, "%s(%u)", As_Meter(meter)->name, meter->param); diff --git a/ProcessList.c b/ProcessList.c index d11567893..9d213d1d8 100644 --- a/ProcessList.c +++ b/ProcessList.c @@ -20,7 +20,7 @@ in the source distribution for its full text. #include "XUtils.h" -ProcessList* ProcessList_init(ProcessList* this, const ObjectClass* klass, UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* dynamicColumns, Hashtable* pidMatchList, uid_t userId) { +ProcessList* ProcessList_init(ProcessList* this, const ObjectClass* klass, UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) { this->processes = Vector_new(klass, true, DEFAULT_SIZE); this->displayList = Vector_new(klass, false, DEFAULT_SIZE); @@ -29,8 +29,6 @@ ProcessList* ProcessList_init(ProcessList* this, const ObjectClass* klass, Users this->usersTable = usersTable; this->pidMatchList = pidMatchList; - this->dynamicMeters = dynamicMeters; - this->dynamicColumns = dynamicColumns; this->userId = userId; @@ -82,8 +80,9 @@ void ProcessList_setPanel(ProcessList* this, Panel* panel) { this->panel = panel; } -static const char* alignedDynamicColumnTitle(const ProcessList* this, int key, char* titleBuffer, size_t titleBufferSize) { - const DynamicColumn* column = Hashtable_get(this->dynamicColumns, key); +// helper function to fill an aligned title string for a dynamic column +static const char* alignedTitleDynamicColumn(const Settings* settings, int key, char* titleBuffer, size_t titleBufferSize) { + const DynamicColumn* column = Hashtable_get(settings->dynamicColumns, key); if (column == NULL) return "- "; int width = column->width; @@ -93,40 +92,45 @@ static const char* alignedDynamicColumnTitle(const ProcessList* this, int key, c return titleBuffer; } -static const char* alignedProcessFieldTitle(const ProcessList* this, ProcessField field) { - static char titleBuffer[UINT8_MAX + sizeof(" ")]; - assert(sizeof(titleBuffer) >= DYNAMIC_MAX_COLUMN_WIDTH + sizeof(" ")); - assert(sizeof(titleBuffer) >= PROCESS_MAX_PID_DIGITS + sizeof(" ")); - assert(sizeof(titleBuffer) >= PROCESS_MAX_UID_DIGITS + sizeof(" ")); - - if (field >= LAST_PROCESSFIELD) - return alignedDynamicColumnTitle(this, field, titleBuffer, sizeof(titleBuffer)); - +// helper function to fill an aligned title string for a process field +static const char* alignedTitleProcessField(ProcessField field, char* titleBuffer, size_t titleBufferSize) { const char* title = Process_fields[field].title; if (!title) return "- "; if (Process_fields[field].pidColumn) { - xSnprintf(titleBuffer, sizeof(titleBuffer), "%*s ", Process_pidDigits, title); + xSnprintf(titleBuffer, titleBufferSize, "%*s ", Process_pidDigits, title); return titleBuffer; } if (field == ST_UID) { - xSnprintf(titleBuffer, sizeof(titleBuffer), "%*s ", Process_uidDigits, title); + xSnprintf(titleBuffer, titleBufferSize, "%*s ", Process_uidDigits, title); return titleBuffer; } if (Process_fields[field].autoWidth) { if (field == PERCENT_CPU) - xSnprintf(titleBuffer, sizeof(titleBuffer), "%*s ", Process_fieldWidths[field], title); + xSnprintf(titleBuffer, titleBufferSize, "%*s ", Process_fieldWidths[field], title); else - xSnprintf(titleBuffer, sizeof(titleBuffer), "%-*.*s ", Process_fieldWidths[field], Process_fieldWidths[field], title); + xSnprintf(titleBuffer, titleBufferSize, "%-*.*s ", Process_fieldWidths[field], Process_fieldWidths[field], title); return titleBuffer; } return title; } +// helper function to create an aligned title string for a given field +static const char* ProcessField_alignedTitle(const Settings* settings, ProcessField field) { + static char titleBuffer[UINT8_MAX + sizeof(" ")]; + assert(sizeof(titleBuffer) >= DYNAMIC_MAX_COLUMN_WIDTH + sizeof(" ")); + assert(sizeof(titleBuffer) >= PROCESS_MAX_PID_DIGITS + sizeof(" ")); + assert(sizeof(titleBuffer) >= PROCESS_MAX_UID_DIGITS + sizeof(" ")); + + if (field < LAST_PROCESSFIELD) + return alignedTitleProcessField(field, titleBuffer, sizeof(titleBuffer)); + return alignedTitleDynamicColumn(settings, field, titleBuffer, sizeof(titleBuffer)); +} + void ProcessList_printHeader(const ProcessList* this, RichString* header) { RichString_rewind(header, RichString_size(header)); @@ -146,7 +150,7 @@ void ProcessList_printHeader(const ProcessList* this, RichString* header) { color = CRT_colors[PANEL_HEADER_FOCUS]; } - RichString_appendWide(header, color, alignedProcessFieldTitle(this, fields[i])); + RichString_appendWide(header, color, ProcessField_alignedTitle(settings, fields[i])); if (key == fields[i] && RichString_getCharVal(*header, RichString_size(header) - 1) == ' ') { bool ascending = ScreenSettings_getActiveDirection(ss) == 1; RichString_rewind(header, 1); // rewind to override space @@ -337,10 +341,11 @@ void ProcessList_updateDisplayList(ProcessList* this) { ProcessField ProcessList_keyAt(const ProcessList* this, int at) { int x = 0; - const ProcessField* fields = this->settings->ss->fields; + const Settings* settings = this->settings; + const ProcessField* fields = settings->ss->fields; ProcessField field; for (int i = 0; (field = fields[i]); i++) { - int len = strlen(alignedProcessFieldTitle(this, field)); + int len = strlen(ProcessField_alignedTitle(settings, field)); if (at >= x && at <= x + len) { return field; } diff --git a/ProcessList.h b/ProcessList.h index 419dea89a..eab122a3e 100644 --- a/ProcessList.h +++ b/ProcessList.h @@ -51,9 +51,6 @@ typedef struct ProcessList_ { bool needsSort; - Hashtable* dynamicMeters; /* runtime-discovered meters */ - Hashtable* dynamicColumns; /* runtime-discovered Columns */ - struct timeval realtime; /* time of the current sample */ uint64_t realtimeMs; /* current time in milliseconds */ uint64_t monotonicMs; /* same, but from monotonic clock */ @@ -90,13 +87,13 @@ typedef struct ProcessList_ { } ProcessList; /* Implemented by platforms */ -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* dynamicColumns, Hashtable* pidMatchList, uid_t userId); +ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId); void ProcessList_delete(ProcessList* pl); void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate); bool ProcessList_isCPUonline(const ProcessList* super, unsigned int id); -ProcessList* ProcessList_init(ProcessList* this, const ObjectClass* klass, UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* dynamicColumns, Hashtable* pidMatchList, uid_t userId); +ProcessList* ProcessList_init(ProcessList* this, const ObjectClass* klass, UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId); void ProcessList_done(ProcessList* this); diff --git a/Settings.c b/Settings.c index 8543b9e43..c712966e3 100644 --- a/Settings.c +++ b/Settings.c @@ -667,10 +667,11 @@ int Settings_write(const Settings* this, bool onCrash) { return r; } -Settings* Settings_new(unsigned int initialCpuCount, Hashtable* dynamicColumns) { +Settings* Settings_new(unsigned int initialCpuCount, Hashtable* dynamicMeters, Hashtable* dynamicColumns) { Settings* this = xCalloc(1, sizeof(Settings)); this->dynamicColumns = dynamicColumns; + this->dynamicMeters = dynamicMeters; this->hLayout = HF_TWO_50_50; this->hColumns = xCalloc(HeaderLayout_getColumns(this->hLayout), sizeof(MeterColumnSetting)); diff --git a/Settings.h b/Settings.h index baf05da3b..48c62590a 100644 --- a/Settings.h +++ b/Settings.h @@ -51,7 +51,8 @@ typedef struct Settings_ { int config_version; HeaderLayout hLayout; MeterColumnSetting* hColumns; - Hashtable* dynamicColumns; + Hashtable* dynamicColumns; /* runtime-discovered columns */ + Hashtable* dynamicMeters; /* runtime-discovered meters */ ScreenSettings** screens; unsigned int nScreens; @@ -117,7 +118,7 @@ void Settings_delete(Settings* this); int Settings_write(const Settings* this, bool onCrash); -Settings* Settings_new(unsigned int initialCpuCount, Hashtable* dynamicColumns); +Settings* Settings_new(unsigned int initialCpuCount, Hashtable* dynamicMeters, Hashtable* dynamicColumns); ScreenSettings* Settings_newScreen(Settings* this, const ScreenDefaults* defaults); diff --git a/darwin/DarwinProcessList.c b/darwin/DarwinProcessList.c index dae588ba3..f7f1e592a 100644 --- a/darwin/DarwinProcessList.c +++ b/darwin/DarwinProcessList.c @@ -89,10 +89,10 @@ static struct kinfo_proc* ProcessList_getKInfoProcs(size_t* count) { CRT_fatalError("Unable to get kinfo_procs"); } -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* dynamicColumns, Hashtable* pidMatchList, uid_t userId) { +ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) { DarwinProcessList* this = xCalloc(1, sizeof(DarwinProcessList)); - ProcessList_init(&this->super, Class(DarwinProcess), usersTable, dynamicMeters, dynamicColumns, pidMatchList, userId); + ProcessList_init(&this->super, Class(DarwinProcess), usersTable, pidMatchList, userId); /* Initialize the CPU information */ this->super.activeCPUs = ProcessList_allocateCPULoadInfo(&this->prev_load); diff --git a/darwin/DarwinProcessList.h b/darwin/DarwinProcessList.h index 393e65674..ec504bcfe 100644 --- a/darwin/DarwinProcessList.h +++ b/darwin/DarwinProcessList.h @@ -28,7 +28,7 @@ typedef struct DarwinProcessList_ { ZfsArcStats zfs; } DarwinProcessList; -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* dynamicColumns, Hashtable* pidMatchList, uid_t userId); +ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId); void ProcessList_delete(ProcessList* this); diff --git a/dragonflybsd/DragonFlyBSDProcessList.c b/dragonflybsd/DragonFlyBSDProcessList.c index f46d6cece..39b052507 100644 --- a/dragonflybsd/DragonFlyBSDProcessList.c +++ b/dragonflybsd/DragonFlyBSDProcessList.c @@ -42,12 +42,12 @@ static int MIB_kern_cp_time[2]; static int MIB_kern_cp_times[2]; static int kernelFScale; -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* dynamicColumns, Hashtable* pidMatchList, uid_t userId) { +ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) { size_t len; char errbuf[_POSIX2_LINE_MAX]; DragonFlyBSDProcessList* dfpl = xCalloc(1, sizeof(DragonFlyBSDProcessList)); ProcessList* pl = (ProcessList*) dfpl; - ProcessList_init(pl, Class(DragonFlyBSDProcess), usersTable, dynamicMeters, dynamicColumns, pidMatchList, userId); + ProcessList_init(pl, Class(DragonFlyBSDProcess), usersTable, pidMatchList, userId); // physical memory in system: hw.physmem // physical page size: hw.pagesize diff --git a/dragonflybsd/DragonFlyBSDProcessList.h b/dragonflybsd/DragonFlyBSDProcessList.h index c1bf2d19c..2cc400545 100644 --- a/dragonflybsd/DragonFlyBSDProcessList.h +++ b/dragonflybsd/DragonFlyBSDProcessList.h @@ -53,7 +53,7 @@ typedef struct DragonFlyBSDProcessList_ { Hashtable* jails; } DragonFlyBSDProcessList; -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* dynamicColumns, Hashtable* pidMatchList, uid_t userId); +ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId); void ProcessList_delete(ProcessList* this); diff --git a/freebsd/FreeBSDProcessList.c b/freebsd/FreeBSDProcessList.c index 331aac617..f545a3eee 100644 --- a/freebsd/FreeBSDProcessList.c +++ b/freebsd/FreeBSDProcessList.c @@ -59,12 +59,12 @@ static int MIB_kern_cp_time[2]; static int MIB_kern_cp_times[2]; static int kernelFScale; -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* DynamicColumns, Hashtable* pidMatchList, uid_t userId) { +ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) { size_t len; char errbuf[_POSIX2_LINE_MAX]; FreeBSDProcessList* fpl = xCalloc(1, sizeof(FreeBSDProcessList)); ProcessList* pl = (ProcessList*) fpl; - ProcessList_init(pl, Class(FreeBSDProcess), usersTable, dynamicMeters, DynamicColumns, pidMatchList, userId); + ProcessList_init(pl, Class(FreeBSDProcess), usersTable, pidMatchList, userId); // physical memory in system: hw.physmem // physical page size: hw.pagesize diff --git a/freebsd/FreeBSDProcessList.h b/freebsd/FreeBSDProcessList.h index adc70e4f5..98510fadc 100644 --- a/freebsd/FreeBSDProcessList.h +++ b/freebsd/FreeBSDProcessList.h @@ -47,7 +47,7 @@ typedef struct FreeBSDProcessList_ { } FreeBSDProcessList; -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* dynamicColumns, Hashtable* pidMatchList, uid_t userId); +ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId); void ProcessList_delete(ProcessList* this); diff --git a/linux/LinuxProcessList.c b/linux/LinuxProcessList.c index 0dbabe5ba..9d1f79338 100644 --- a/linux/LinuxProcessList.c +++ b/linux/LinuxProcessList.c @@ -297,11 +297,11 @@ static void LinuxProcessList_updateCPUcount(ProcessList* super) { super->existingCPUs = currExisting; } -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* dynamicColumns, Hashtable* pidMatchList, uid_t userId) { +ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) { LinuxProcessList* this = xCalloc(1, sizeof(LinuxProcessList)); ProcessList* pl = &(this->super); - ProcessList_init(pl, Class(LinuxProcess), usersTable, dynamicMeters, dynamicColumns, pidMatchList, userId); + ProcessList_init(pl, Class(LinuxProcess), usersTable, pidMatchList, userId); LinuxProcessList_initTtyDrivers(this); // Initialize page size diff --git a/linux/LinuxProcessList.h b/linux/LinuxProcessList.h index 6c2f7dbe3..1b29b3bde 100644 --- a/linux/LinuxProcessList.h +++ b/linux/LinuxProcessList.h @@ -115,7 +115,7 @@ typedef struct LinuxProcessList_ { #define PROC_LINE_LENGTH 4096 #endif -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* dynamicColumns, Hashtable* pidMatchList, uid_t userId); +ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId); void ProcessList_delete(ProcessList* pl); diff --git a/netbsd/NetBSDProcessList.c b/netbsd/NetBSDProcessList.c index 197a150b5..381672d65 100644 --- a/netbsd/NetBSDProcessList.c +++ b/netbsd/NetBSDProcessList.c @@ -106,14 +106,14 @@ static void NetBSDProcessList_updateCPUcount(ProcessList* super) { } } -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* dynamicColumns, Hashtable* pidMatchList, uid_t userId) { +ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) { const int fmib[] = { CTL_KERN, KERN_FSCALE }; size_t size; char errbuf[_POSIX2_LINE_MAX]; NetBSDProcessList* npl = xCalloc(1, sizeof(NetBSDProcessList)); ProcessList* pl = (ProcessList*) npl; - ProcessList_init(pl, Class(NetBSDProcess), usersTable, dynamicMeters, dynamicColumns, pidMatchList, userId); + ProcessList_init(pl, Class(NetBSDProcess), usersTable, pidMatchList, userId); NetBSDProcessList_updateCPUcount(pl); diff --git a/netbsd/NetBSDProcessList.h b/netbsd/NetBSDProcessList.h index d228f4879..d5a757fe5 100644 --- a/netbsd/NetBSDProcessList.h +++ b/netbsd/NetBSDProcessList.h @@ -49,7 +49,7 @@ typedef struct NetBSDProcessList_ { } NetBSDProcessList; -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* dynamicColumns, Hashtable* pidMatchList, uid_t userId); +ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId); void ProcessList_delete(ProcessList* this); diff --git a/openbsd/OpenBSDProcessList.c b/openbsd/OpenBSDProcessList.c index d070e5e9e..6ad3a7607 100644 --- a/openbsd/OpenBSDProcessList.c +++ b/openbsd/OpenBSDProcessList.c @@ -94,14 +94,14 @@ static void OpenBSDProcessList_updateCPUcount(ProcessList* super) { } -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* dynamicColumns, Hashtable* pidMatchList, uid_t userId) { +ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) { const int fmib[] = { CTL_KERN, KERN_FSCALE }; size_t size; char errbuf[_POSIX2_LINE_MAX]; OpenBSDProcessList* opl = xCalloc(1, sizeof(OpenBSDProcessList)); ProcessList* pl = (ProcessList*) opl; - ProcessList_init(pl, Class(OpenBSDProcess), usersTable, dynamicMeters, dynamicColumns, pidMatchList, userId); + ProcessList_init(pl, Class(OpenBSDProcess), usersTable, pidMatchList, userId); OpenBSDProcessList_updateCPUcount(pl); diff --git a/openbsd/OpenBSDProcessList.h b/openbsd/OpenBSDProcessList.h index 89fdb0998..99de96cd1 100644 --- a/openbsd/OpenBSDProcessList.h +++ b/openbsd/OpenBSDProcessList.h @@ -49,7 +49,7 @@ typedef struct OpenBSDProcessList_ { } OpenBSDProcessList; -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* dynamicColumns, Hashtable* pidMatchList, uid_t userId); +ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId); void ProcessList_delete(ProcessList* this); diff --git a/pcp/PCPDynamicColumn.c b/pcp/PCPDynamicColumn.c index 33c6d72a8..8c35fc10e 100644 --- a/pcp/PCPDynamicColumn.c +++ b/pcp/PCPDynamicColumn.c @@ -304,7 +304,7 @@ void PCPDynamicColumn_writeField(PCPDynamicColumn* this, const Process* proc, Ri } int PCPDynamicColumn_compareByKey(const PCPProcess* p1, const PCPProcess* p2, ProcessField key) { - const PCPDynamicColumn* column = Hashtable_get(p1->super.processList->dynamicColumns, key); + const PCPDynamicColumn* column = Hashtable_get(p1->super.settings->dynamicColumns, key); if (!column) return -1; diff --git a/pcp/PCPProcessList.c b/pcp/PCPProcessList.c index f893689a1..8147beebb 100644 --- a/pcp/PCPProcessList.c +++ b/pcp/PCPProcessList.c @@ -63,11 +63,11 @@ static char* setUser(UsersTable* this, unsigned int uid, int pid, int offset) { return name; } -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* dynamicColumns, Hashtable* pidMatchList, uid_t userId) { +ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) { PCPProcessList* this = xCalloc(1, sizeof(PCPProcessList)); ProcessList* super = &(this->super); - ProcessList_init(super, Class(PCPProcess), usersTable, dynamicMeters, dynamicColumns, pidMatchList, userId); + ProcessList_init(super, Class(PCPProcess), usersTable, pidMatchList, userId); struct timeval timestamp; gettimeofday(×tamp, NULL); diff --git a/pcp/PCPProcessList.h b/pcp/PCPProcessList.h index a3a7372af..90e9939c1 100644 --- a/pcp/PCPProcessList.h +++ b/pcp/PCPProcessList.h @@ -63,7 +63,7 @@ typedef struct PCPProcessList_ { ZfsArcStats zfs; } PCPProcessList; -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* dynamicColumns, Hashtable* pidMatchList, uid_t userId); +ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId); void ProcessList_delete(ProcessList* pl); diff --git a/solaris/SolarisProcessList.c b/solaris/SolarisProcessList.c index 905cfbd47..409079895 100644 --- a/solaris/SolarisProcessList.c +++ b/solaris/SolarisProcessList.c @@ -89,10 +89,10 @@ static void SolarisProcessList_updateCPUcount(ProcessList* super) { } } -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* dynamicColumns, Hashtable* pidMatchList, uid_t userId) { +ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) { SolarisProcessList* spl = xCalloc(1, sizeof(SolarisProcessList)); ProcessList* pl = (ProcessList*) spl; - ProcessList_init(pl, Class(SolarisProcess), usersTable, dynamicMeters, dynamicColumns, pidMatchList, userId); + ProcessList_init(pl, Class(SolarisProcess), usersTable, pidMatchList, userId); spl->kd = kstat_open(); if (!spl->kd) diff --git a/solaris/SolarisProcessList.h b/solaris/SolarisProcessList.h index 91fd4f413..e2f4f683f 100644 --- a/solaris/SolarisProcessList.h +++ b/solaris/SolarisProcessList.h @@ -54,7 +54,7 @@ typedef struct SolarisProcessList_ { ZfsArcStats zfs; } SolarisProcessList; -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* dynamicColumns, Hashtable* pidMatchList, uid_t userId); +ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId); void ProcessList_delete(ProcessList* pl); diff --git a/unsupported/UnsupportedProcessList.c b/unsupported/UnsupportedProcessList.c index 5291797f3..fc226f76f 100644 --- a/unsupported/UnsupportedProcessList.c +++ b/unsupported/UnsupportedProcessList.c @@ -14,9 +14,9 @@ in the source distribution for its full text. #include "UnsupportedProcess.h" -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* dynamicColumns, Hashtable* pidMatchList, uid_t userId) { +ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) { ProcessList* this = xCalloc(1, sizeof(ProcessList)); - ProcessList_init(this, Class(Process), usersTable, dynamicMeters, dynamicColumns, pidMatchList, userId); + ProcessList_init(this, Class(Process), usersTable, pidMatchList, userId); this->existingCPUs = 1; this->activeCPUs = 1; diff --git a/unsupported/UnsupportedProcessList.h b/unsupported/UnsupportedProcessList.h index cbf25afa5..9f4d23aae 100644 --- a/unsupported/UnsupportedProcessList.h +++ b/unsupported/UnsupportedProcessList.h @@ -10,7 +10,7 @@ in the source distribution for its full text. #include "ProcessList.h" -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* dynamicColumns, Hashtable* pidMatchList, uid_t userId); +ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId); void ProcessList_delete(ProcessList* this); From 14da84f877a0f3627ddaea836c9537b2292fa22d Mon Sep 17 00:00:00 2001 From: Nathan Scott Date: Tue, 4 Apr 2023 16:47:11 +1000 Subject: [PATCH 072/106] Refactor to use a program name pointer instead of PACKAGE macro During SEGV handling under pcp-htop several incorrect strings were observed - in particular reporting htop as the binary name instead of pcp-htop. This is something we want to be crystal clear on when we request user stack traces etc, so make sure this cannot be done incorrectly. --- CRT.c | 20 ++++++++++++-------- CommandLine.c | 6 +++--- CommandLine.h | 4 +++- htop.c | 4 +++- pcp-htop.c | 7 ++++--- 5 files changed, 25 insertions(+), 16 deletions(-) diff --git a/CRT.c b/CRT.c index 3c062919d..50f0c0e80 100644 --- a/CRT.c +++ b/CRT.c @@ -19,6 +19,7 @@ in the source distribution for its full text. #include #include +#include "CommandLine.h" #include "ProvideCurses.h" #include "XUtils.h" @@ -1153,10 +1154,11 @@ void CRT_handleSIGSEGV(int signal) { "============================\n" "Please check at https://htop.dev/issues whether this issue has already been reported.\n" "If no similar issue has been reported before, please create a new issue with the following information:\n" - " - Your "PACKAGE" version: '"VERSION"'\n" + " - Your %s version: '"VERSION"'\n" " - Your OS and kernel version (uname -a)\n" " - Your distribution and release (lsb_release -a)\n" - " - Likely steps to reproduce (How did it happen?)\n" + " - Likely steps to reproduce (How did it happen?)\n", + program ); #ifdef PRINT_BACKTRACE @@ -1196,15 +1198,16 @@ void CRT_handleSIGSEGV(int signal) { fprintf(stderr, "\n" "To make the above information more practical to work with, " - "please also provide a disassembly of your "PACKAGE" binary. " + "please also provide a disassembly of your %s binary. " "This can usually be done by running the following command:\n" - "\n" + "\n", + program ); #ifdef HTOP_DARWIN - fprintf(stderr, " otool -tvV `which "PACKAGE"` > ~/htop.otool\n"); + fprintf(stderr, " otool -tvV `which %s` > ~/%s.otool\n", program, program); #else - fprintf(stderr, " objdump -d -S -w `which "PACKAGE"` > ~/htop.objdump\n"); + fprintf(stderr, " objdump -d -S -w `which %s` > ~/%s.objdump\n", program, program); #endif fprintf(stderr, @@ -1216,8 +1219,9 @@ void CRT_handleSIGSEGV(int signal) { fprintf(stderr, "Running this program with debug symbols or inside a debugger may provide further insights.\n" "\n" - "Thank you for helping to improve "PACKAGE"!\n" - "\n" + "Thank you for helping to improve %s!\n" + "\n", + program ); /* Call old sigsegv handler; may be default exit or third party one (e.g. ASAN) */ diff --git a/CommandLine.c b/CommandLine.c index 833592068..2b2a68c2a 100644 --- a/CommandLine.c +++ b/CommandLine.c @@ -89,7 +89,7 @@ typedef struct CommandLineSettings_ { bool readonly; } CommandLineSettings; -static CommandLineStatus parseArguments(const char* program, int argc, char** argv, CommandLineSettings* flags) { +static CommandLineStatus parseArguments(int argc, char** argv, CommandLineSettings* flags) { *flags = (CommandLineSettings) { .pidMatchList = NULL, @@ -298,7 +298,7 @@ static void setCommFilter(State* state, char** commFilter) { *commFilter = NULL; } -int CommandLine_run(const char* name, int argc, char** argv) { +int CommandLine_run(int argc, char** argv) { /* initialize locale */ const char* lc_ctype; @@ -310,7 +310,7 @@ int CommandLine_run(const char* name, int argc, char** argv) { CommandLineStatus status = STATUS_OK; CommandLineSettings flags = { 0 }; - if ((status = parseArguments(name, argc, argv, &flags)) != STATUS_OK) + if ((status = parseArguments(argc, argv, &flags)) != STATUS_OK) return status != STATUS_OK_EXIT ? 1 : 0; if (flags.readonly) diff --git a/CommandLine.h b/CommandLine.h index fbdede84b..18395005b 100644 --- a/CommandLine.h +++ b/CommandLine.h @@ -14,6 +14,8 @@ typedef enum { STATUS_OK_EXIT } CommandLineStatus; -int CommandLine_run(const char* name, int argc, char** argv); +extern const char* program; + +int CommandLine_run(int argc, char** argv); #endif diff --git a/htop.c b/htop.c index 6b9ea48a2..715579380 100644 --- a/htop.c +++ b/htop.c @@ -11,6 +11,8 @@ in the source distribution for its full text. #include "CommandLine.h" +const char* program = PACKAGE; + int main(int argc, char** argv) { - return CommandLine_run(PACKAGE, argc, argv); + return CommandLine_run(argc, argv); } diff --git a/pcp-htop.c b/pcp-htop.c index 2713c896c..ed585ecbe 100644 --- a/pcp-htop.c +++ b/pcp-htop.c @@ -14,13 +14,14 @@ in the source distribution for its full text. #include "Platform.h" +const char* program = "pcp-htop"; + int main(int argc, char** argv) { - const char* name = "pcp-htop"; - pmSetProgname(name); + pmSetProgname(program); /* extract environment variables */ opts.flags |= PM_OPTFLAG_ENV_ONLY; (void)pmGetOptions(argc, argv, &opts); - return CommandLine_run(name, argc, argv); + return CommandLine_run(argc, argv); } From c878343784f23d7cb18ccec6aa034f01aee8069e Mon Sep 17 00:00:00 2001 From: Nathan Scott Date: Tue, 4 Apr 2023 16:48:46 +1000 Subject: [PATCH 073/106] Correct some inconsistent function call conventions in CRT.c --- CRT.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/CRT.c b/CRT.c index 50f0c0e80..2bc9d13f3 100644 --- a/CRT.c +++ b/CRT.c @@ -888,16 +888,16 @@ static struct sigaction old_sig_handler[32]; static void CRT_installSignalHandlers(void) { struct sigaction act; - sigemptyset (&act.sa_mask); + sigemptyset(&act.sa_mask); act.sa_flags = (int)SA_RESETHAND | SA_NODEFER; act.sa_handler = CRT_handleSIGSEGV; - sigaction (SIGSEGV, &act, &old_sig_handler[SIGSEGV]); - sigaction (SIGFPE, &act, &old_sig_handler[SIGFPE]); - sigaction (SIGILL, &act, &old_sig_handler[SIGILL]); - sigaction (SIGBUS, &act, &old_sig_handler[SIGBUS]); - sigaction (SIGPIPE, &act, &old_sig_handler[SIGPIPE]); - sigaction (SIGSYS, &act, &old_sig_handler[SIGSYS]); - sigaction (SIGABRT, &act, &old_sig_handler[SIGABRT]); + sigaction(SIGSEGV, &act, &old_sig_handler[SIGSEGV]); + sigaction(SIGFPE, &act, &old_sig_handler[SIGFPE]); + sigaction(SIGILL, &act, &old_sig_handler[SIGILL]); + sigaction(SIGBUS, &act, &old_sig_handler[SIGBUS]); + sigaction(SIGPIPE, &act, &old_sig_handler[SIGPIPE]); + sigaction(SIGSYS, &act, &old_sig_handler[SIGSYS]); + sigaction(SIGABRT, &act, &old_sig_handler[SIGABRT]); signal(SIGCHLD, SIG_DFL); signal(SIGINT, CRT_handleSIGTERM); @@ -906,13 +906,13 @@ static void CRT_installSignalHandlers(void) { } void CRT_resetSignalHandlers(void) { - sigaction (SIGSEGV, &old_sig_handler[SIGSEGV], NULL); - sigaction (SIGFPE, &old_sig_handler[SIGFPE], NULL); - sigaction (SIGILL, &old_sig_handler[SIGILL], NULL); - sigaction (SIGBUS, &old_sig_handler[SIGBUS], NULL); - sigaction (SIGPIPE, &old_sig_handler[SIGPIPE], NULL); - sigaction (SIGSYS, &old_sig_handler[SIGSYS], NULL); - sigaction (SIGABRT, &old_sig_handler[SIGABRT], NULL); + sigaction(SIGSEGV, &old_sig_handler[SIGSEGV], NULL); + sigaction(SIGFPE, &old_sig_handler[SIGFPE], NULL); + sigaction(SIGILL, &old_sig_handler[SIGILL], NULL); + sigaction(SIGBUS, &old_sig_handler[SIGBUS], NULL); + sigaction(SIGPIPE, &old_sig_handler[SIGPIPE], NULL); + sigaction(SIGSYS, &old_sig_handler[SIGSYS], NULL); + sigaction(SIGABRT, &old_sig_handler[SIGABRT], NULL); signal(SIGINT, SIG_DFL); signal(SIGTERM, SIG_DFL); @@ -1225,7 +1225,7 @@ void CRT_handleSIGSEGV(int signal) { ); /* Call old sigsegv handler; may be default exit or third party one (e.g. ASAN) */ - if (sigaction (signal, &old_sig_handler[signal], NULL) < 0) { + if (sigaction(signal, &old_sig_handler[signal], NULL) < 0) { /* This avoids an infinite loop in case the handler could not be reset. */ fprintf(stderr, "!!! Chained handler could not be restored. Forcing exit.\n" From da255cb33fd8ddd233232850f9465e6ddb47015f Mon Sep 17 00:00:00 2001 From: Benny Baumann Date: Tue, 11 Apr 2023 22:49:39 +0200 Subject: [PATCH 074/106] Minor code formatting consistency fixes --- Action.c | 2 +- FileDescriptorMeter.c | 10 +++++----- MemoryMeter.h | 12 ++++++------ ScreensPanel.h | 2 +- SwapMeter.h | 6 +++--- TraceScreen.c | 2 +- linux/LinuxProcessList.c | 4 ++-- linux/Platform.c | 8 ++++---- linux/SystemdMeter.c | 6 +++--- netbsd/Platform.c | 2 +- pcp/PCPProcessList.c | 2 +- 11 files changed, 28 insertions(+), 28 deletions(-) diff --git a/Action.c b/Action.c index 5d33f6624..a67233cca 100644 --- a/Action.c +++ b/Action.c @@ -419,7 +419,7 @@ static Htop_Reaction actionSetSchedPolicy(State* st) { Panel* schedPanel = Scheduling_newPolicyPanel(preSelectedPolicy); const ListItem* policy; - for(;;) { + for (;;) { policy = (const ListItem*) Action_pickFromVector(st, schedPanel, 18, true); if (!policy || policy->key != -1) diff --git a/FileDescriptorMeter.c b/FileDescriptorMeter.c index e11637105..2d939d66d 100644 --- a/FileDescriptorMeter.c +++ b/FileDescriptorMeter.c @@ -47,12 +47,12 @@ static void FileDescriptorMeter_updateValues(Meter* this) { * 3. If the maximum is effectively unlimited (AKA > 1<<30), * Do the same as for 2, but cap at 1<<30. */ - if (this->values[1] <= 1<<16) { + if (this->values[1] <= 1 << 16) { this->total = this->values[1]; } else { if (this->total < 16 * this->values[0]) { - for (this->total = 1<<16; this->total < 16 * this->values[0]; this->total *= 2) { - if (this->total >= 1<<30) { + for (this->total = 1 << 16; this->total < 16 * this->values[0]; this->total *= 2) { + if (this->total >= 1 << 30) { break; } } @@ -62,8 +62,8 @@ static void FileDescriptorMeter_updateValues(Meter* this) { this->total = this->values[1]; } - if (this->total > 1<<30) { - this->total = 1<<30; + if (this->total > 1 << 30) { + this->total = 1 << 30; } } diff --git a/MemoryMeter.h b/MemoryMeter.h index cc2a160c5..d8f5fdbc9 100644 --- a/MemoryMeter.h +++ b/MemoryMeter.h @@ -10,12 +10,12 @@ in the source distribution for its full text. #include "Meter.h" typedef enum { - MEMORY_METER_USED = 0, - MEMORY_METER_BUFFERS = 1, - MEMORY_METER_SHARED = 2, - MEMORY_METER_CACHE = 3, - MEMORY_METER_AVAILABLE = 4, - MEMORY_METER_ITEMCOUNT = 5, // number of entries in this enum + MEMORY_METER_USED = 0, + MEMORY_METER_BUFFERS = 1, + MEMORY_METER_SHARED = 2, + MEMORY_METER_CACHE = 3, + MEMORY_METER_AVAILABLE = 4, + MEMORY_METER_ITEMCOUNT = 5, // number of entries in this enum } MemoryMeterValues; extern const MeterClass MemoryMeter_class; diff --git a/ScreensPanel.h b/ScreensPanel.h index 88d85b5c0..60aaf2a2d 100644 --- a/ScreensPanel.h +++ b/ScreensPanel.h @@ -31,7 +31,7 @@ typedef struct ScreensPanel_ { char* saved; int cursor; bool moving; - ListItem *renamingItem; + ListItem* renamingItem; } ScreensPanel; typedef struct ScreenListItem_ { diff --git a/SwapMeter.h b/SwapMeter.h index b71e83fee..162897fb7 100644 --- a/SwapMeter.h +++ b/SwapMeter.h @@ -10,9 +10,9 @@ in the source distribution for its full text. #include "Meter.h" typedef enum { - SWAP_METER_USED = 0, - SWAP_METER_CACHE = 1, - SWAP_METER_ITEMCOUNT = 2, // number of entries in this enum + SWAP_METER_USED = 0, + SWAP_METER_CACHE = 1, + SWAP_METER_ITEMCOUNT = 2, // number of entries in this enum } SwapMeterValues; extern const MeterClass SwapMeter_class; diff --git a/TraceScreen.c b/TraceScreen.c index 2aa0781bd..e8f55ff56 100644 --- a/TraceScreen.c +++ b/TraceScreen.c @@ -92,7 +92,7 @@ bool TraceScreen_forkTracer(TraceScreen* this) { xSnprintf(buffer, sizeof(buffer), "%d", this->super.process->pid); // Use of NULL in variadic functions must have a pointer cast. // The NULL constant is not required by standard to have a pointer type. - execlp("strace", "strace", "-T", "-tt", "-s", "512", "-p", buffer, (char *)NULL); + execlp("strace", "strace", "-T", "-tt", "-s", "512", "-p", buffer, (char*)NULL); // Should never reach here, unless execlp fails ... const char* message = "Could not execute 'strace'. Please make sure it is available in your $PATH."; diff --git a/linux/LinuxProcessList.c b/linux/LinuxProcessList.c index 9d1f79338..a3472c535 100644 --- a/linux/LinuxProcessList.c +++ b/linux/LinuxProcessList.c @@ -512,7 +512,7 @@ static bool LinuxProcessList_readStatFile(LinuxProcess* lp, openat_arg_t procFd, } static bool LinuxProcessList_readStatusFile(Process* process, openat_arg_t procFd) { - LinuxProcess* lp = (LinuxProcess*) process; + LinuxProcess* lp = (LinuxProcess*) process; unsigned long ctxt = 0; #ifdef HAVE_VSERVER @@ -613,7 +613,7 @@ static bool LinuxProcessList_updateUser(ProcessList* processList, Process* proce } static void LinuxProcessList_readIoFile(LinuxProcess* lp, openat_arg_t procFd, bool scanMainThread, unsigned long long realtimeMs) { - Process *process = &lp->super; + Process* process = &lp->super; char path[20] = "io"; char buffer[1024]; if (scanMainThread) { diff --git a/linux/Platform.c b/linux/Platform.c index 92da08581..0ab2be4fc 100644 --- a/linux/Platform.c +++ b/linux/Platform.c @@ -458,16 +458,16 @@ FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) { continue; errno = 0; - char *end = de->d_name; + char* end = de->d_name; int file = strtoull(de->d_name, &end, 10); if (errno || *end) continue; int fd = openat(dfd, de->d_name, O_RDONLY | O_CLOEXEC); - if(fd == -1) + if (fd == -1) continue; - FILE *f = fdopen(fd, "r"); - if(!f) { + FILE* f = fdopen(fd, "r"); + if (!f) { close(fd); continue; } diff --git a/linux/SystemdMeter.c b/linux/SystemdMeter.c index 848d1e4ef..ff178e0ef 100644 --- a/linux/SystemdMeter.c +++ b/linux/SystemdMeter.c @@ -123,9 +123,9 @@ static int updateViaLib(bool user) { /* Connect to the system bus */ if (!ctx->bus) { if (user) { - r = sym_sd_bus_open_user(&ctx->bus); + r = sym_sd_bus_open_user(&ctx->bus); } else { - r = sym_sd_bus_open_system(&ctx->bus); + r = sym_sd_bus_open_system(&ctx->bus); } if (r < 0) goto busfailure; @@ -246,7 +246,7 @@ static void updateViaExec(bool user) { "--property=NNames", "--property=NJobs", "--property=NInstalledJobs", - (char *)NULL); + (char*)NULL); exit(127); } close(fdpair[1]); diff --git a/netbsd/Platform.c b/netbsd/Platform.c index a79f9c0b7..9c9546fec 100644 --- a/netbsd/Platform.c +++ b/netbsd/Platform.c @@ -413,7 +413,7 @@ bool Platform_getNetworkIO(NetworkIOData* data) { if (ifa->ifa_flags & IFF_LOOPBACK) continue; - const struct if_data* ifd = (const struct if_data *)ifa->ifa_data; + const struct if_data* ifd = (const struct if_data*)ifa->ifa_data; data->bytesReceived += ifd->ifi_ibytes; data->packetsReceived += ifd->ifi_ipackets; diff --git a/pcp/PCPProcessList.c b/pcp/PCPProcessList.c index 8147beebb..a5eb64d66 100644 --- a/pcp/PCPProcessList.c +++ b/pcp/PCPProcessList.c @@ -44,7 +44,7 @@ static void PCPProcessList_updateCPUcount(PCPProcessList* this) { free(this->percpu); free(this->values); - this->percpu = xCalloc(cpus, sizeof(pmAtomValue *)); + this->percpu = xCalloc(cpus, sizeof(pmAtomValue*)); for (unsigned int i = 0; i < cpus; i++) this->percpu[i] = xCalloc(CPU_METRIC_COUNT, sizeof(pmAtomValue)); this->values = xCalloc(cpus, sizeof(pmAtomValue)); From ccf745ee7ea017230827ad0c5332d1e14fa3ee10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= Date: Sun, 9 Apr 2023 14:18:26 +0200 Subject: [PATCH 075/106] Meter: introduce comprisedValues option Useful for bar mode if latter values of the meter comprise previous ones. --- Meter.c | 19 +++++++++++++++---- Meter.h | 2 ++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/Meter.c b/Meter.c index 7f634f19a..a88de04a3 100644 --- a/Meter.c +++ b/Meter.c @@ -219,6 +219,7 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) { assert(startPos + w <= RichString_sizeVal(bar)); int blockSizes[10]; + int blockSizeSum = 0; // First draw in the bar[] buffer... int offset = 0; @@ -230,6 +231,12 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) { } else { blockSizes[i] = 0; } + + if (Meter_comprisedValues(this)) { + blockSizes[i] = MAXIMUM(blockSizes[i] - blockSizeSum, 0); + blockSizeSum += blockSizes[i]; + } + int nextOffset = offset + blockSizes[i]; // (Control against invalid values) nextOffset = CLAMP(nextOffset, 0, w); @@ -323,10 +330,14 @@ static void GraphMeterMode_draw(Meter* this, int x, int y, int w) { for (int i = 0; i < nValues - 1; i++) data->values[i] = data->values[i + 1]; - double value = 0.0; - for (uint8_t i = 0; i < this->curItems; i++) - value += !isnan(this->values[i]) ? this->values[i] : 0; - data->values[nValues - 1] = value; + if (Meter_comprisedValues(this)) { + data->values[nValues - 1] = (this->curItems > 0) ? this->values[this->curItems - 1] : 0.0; + } else { + double value = 0.0; + for (uint8_t i = 0; i < this->curItems; i++) + value += !isnan(this->values[i]) ? this->values[i] : 0; + data->values[nValues - 1] = value; + } } int i = nValues - (w * 2), k = 0; diff --git a/Meter.h b/Meter.h index bd7604a01..8e8618d66 100644 --- a/Meter.h +++ b/Meter.h @@ -74,6 +74,7 @@ typedef struct MeterClass_ { const char* const description; /* optional meter description in header setup menu */ const uint8_t maxItems; const bool isMultiColumn; /* whether the meter draws multiple sub-columns (defaults to false) */ + const bool comprisedValues; /* whether latter values comprise previous ones (defaults to false) */ } MeterClass; #define As_Meter(this_) ((const MeterClass*)((this_)->super.klass)) @@ -94,6 +95,7 @@ typedef struct MeterClass_ { #define Meter_name(this_) As_Meter(this_)->name #define Meter_uiName(this_) As_Meter(this_)->uiName #define Meter_isMultiColumn(this_) As_Meter(this_)->isMultiColumn +#define Meter_comprisedValues(this_) As_Meter(this_)->comprisedValues typedef struct GraphData_ { struct timeval time; From 467bb78bb1cedc1709216ef38424ecb3928263ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= Date: Mon, 3 Apr 2023 20:30:20 +0200 Subject: [PATCH 076/106] ZramMeter: update bar mode Show both compressed and uncompressed bars. Closes: #1216 --- CRT.c | 18 ++++++++++++------ CRT.h | 3 ++- linux/Platform.c | 5 +++-- linux/ZramMeter.c | 20 ++++++++++---------- linux/ZramMeter.h | 5 +++++ 5 files changed, 32 insertions(+), 19 deletions(-) diff --git a/CRT.c b/CRT.c index 2bc9d13f3..85a381159 100644 --- a/CRT.c +++ b/CRT.c @@ -213,7 +213,8 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [ZFS_OTHER] = ColorPair(Magenta, Black), [ZFS_COMPRESSED] = A_BOLD | ColorPair(Blue, Black), [ZFS_RATIO] = ColorPair(Magenta, Black), - [ZRAM] = ColorPair(Yellow, Black), + [ZRAM_COMPRESSED] = ColorPair(Blue, Black), + [ZRAM_UNCOMPRESSED] = ColorPair(Yellow, Black), [DYNAMIC_GRAY] = ColorPairGrayBlack, [DYNAMIC_DARKGRAY] = A_BOLD | ColorPairGrayBlack, [DYNAMIC_RED] = ColorPair(Red, Black), @@ -323,7 +324,8 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [ZFS_OTHER] = A_DIM, [ZFS_COMPRESSED] = A_BOLD, [ZFS_RATIO] = A_BOLD, - [ZRAM] = A_NORMAL, + [ZRAM_COMPRESSED] = A_NORMAL, + [ZRAM_UNCOMPRESSED] = A_NORMAL, [DYNAMIC_GRAY] = A_DIM, [DYNAMIC_DARKGRAY] = A_DIM, [DYNAMIC_RED] = A_BOLD, @@ -433,7 +435,8 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [ZFS_OTHER] = ColorPair(Magenta, White), [ZFS_COMPRESSED] = ColorPair(Cyan, White), [ZFS_RATIO] = ColorPair(Magenta, White), - [ZRAM] = ColorPair(Yellow, White), + [ZRAM_COMPRESSED] = ColorPair(Cyan, White), + [ZRAM_UNCOMPRESSED] = ColorPair(Yellow, White), [DYNAMIC_GRAY] = ColorPair(Black, White), [DYNAMIC_DARKGRAY] = A_BOLD | ColorPair(Black, White), [DYNAMIC_RED] = ColorPair(Red, White), @@ -543,7 +546,8 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [ZFS_OTHER] = A_BOLD | ColorPair(Magenta, Black), [ZFS_COMPRESSED] = ColorPair(Cyan, Black), [ZFS_RATIO] = A_BOLD | ColorPair(Magenta, Black), - [ZRAM] = ColorPair(Yellow, Black), + [ZRAM_COMPRESSED] = ColorPair(Cyan, Black), + [ZRAM_UNCOMPRESSED] = ColorPair(Yellow, Black), [DYNAMIC_GRAY] = ColorPairGrayBlack, [DYNAMIC_DARKGRAY] = A_BOLD | ColorPairGrayBlack, [DYNAMIC_RED] = ColorPair(Red, Black), @@ -653,7 +657,8 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [ZFS_OTHER] = A_BOLD | ColorPair(Magenta, Blue), [ZFS_COMPRESSED] = A_BOLD | ColorPair(White, Blue), [ZFS_RATIO] = A_BOLD | ColorPair(Magenta, Blue), - [ZRAM] = A_BOLD | ColorPair(Yellow, Blue), + [ZRAM_COMPRESSED] = ColorPair(Cyan, Blue), + [ZRAM_UNCOMPRESSED] = ColorPair(Yellow, Blue), [DYNAMIC_GRAY] = ColorPairGrayBlack, [DYNAMIC_DARKGRAY] = A_BOLD | ColorPairGrayBlack, [DYNAMIC_RED] = ColorPair(Red, Blue), @@ -761,7 +766,8 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [ZFS_OTHER] = ColorPair(Magenta, Black), [ZFS_COMPRESSED] = ColorPair(Blue, Black), [ZFS_RATIO] = ColorPair(Magenta, Black), - [ZRAM] = ColorPair(Yellow, Black), + [ZRAM_COMPRESSED] = ColorPair(Blue, Black), + [ZRAM_UNCOMPRESSED] = ColorPair(Yellow, Black), [DYNAMIC_GRAY] = ColorPairGrayBlack, [DYNAMIC_DARKGRAY] = A_BOLD | ColorPairGrayBlack, [DYNAMIC_RED] = ColorPair(Red, Black), diff --git a/CRT.h b/CRT.h index 80de73136..278c1e815 100644 --- a/CRT.h +++ b/CRT.h @@ -138,7 +138,8 @@ typedef enum ColorElements_ { ZFS_OTHER, ZFS_COMPRESSED, ZFS_RATIO, - ZRAM, + ZRAM_COMPRESSED, + ZRAM_UNCOMPRESSED, DYNAMIC_GRAY, DYNAMIC_DARKGRAY, DYNAMIC_RED, diff --git a/linux/Platform.c b/linux/Platform.c index 0ab2be4fc..e004b9b17 100644 --- a/linux/Platform.c +++ b/linux/Platform.c @@ -5,6 +5,7 @@ Released under the GNU GPLv2+, see the COPYING file in the source distribution for its full text. */ +#include "ZramMeter.h" #include "config.h" #include "linux/Platform.h" @@ -382,8 +383,8 @@ void Platform_setSwapValues(Meter* this) { void Platform_setZramValues(Meter* this) { const LinuxProcessList* lpl = (const LinuxProcessList*) this->pl; this->total = lpl->zram.totalZram; - this->values[0] = lpl->zram.usedZramComp; - this->values[1] = lpl->zram.usedZramOrig; + this->values[ZRAM_METER_COMPRESSED] = lpl->zram.usedZramComp; + this->values[ZRAM_METER_UNCOMPRESSED] = lpl->zram.usedZramOrig; } void Platform_setZfsArcValues(Meter* this) { diff --git a/linux/ZramMeter.c b/linux/ZramMeter.c index e1e27b710..6e80eb1a7 100644 --- a/linux/ZramMeter.c +++ b/linux/ZramMeter.c @@ -7,10 +7,12 @@ #include "Object.h" #include "Platform.h" #include "RichString.h" +#include "ZramMeter.h" -static const int ZramMeter_attributes[] = { - ZRAM +static const int ZramMeter_attributes[ZRAM_METER_ITEMCOUNT] = { + [ZRAM_METER_COMPRESSED] = ZRAM_COMPRESSED, + [ZRAM_METER_UNCOMPRESSED] = ZRAM_UNCOMPRESSED, }; static void ZramMeter_updateValues(Meter* this) { @@ -20,15 +22,12 @@ static void ZramMeter_updateValues(Meter* this) { Platform_setZramValues(this); - /* on print bar for compressed data size, not uncompressed */ - this->curItems = 1; - - written = Meter_humanUnit(buffer, this->values[0], size); + written = Meter_humanUnit(buffer, this->values[ZRAM_METER_COMPRESSED], size); METER_BUFFER_CHECK(buffer, size, written); METER_BUFFER_APPEND_CHR(buffer, size, '('); - written = Meter_humanUnit(buffer, this->values[1], size); + written = Meter_humanUnit(buffer, this->values[ZRAM_METER_UNCOMPRESSED], size); METER_BUFFER_CHECK(buffer, size, written); METER_BUFFER_APPEND_CHR(buffer, size, ')'); @@ -47,11 +46,11 @@ static void ZramMeter_display(const Object* cast, RichString* out) { Meter_humanUnit(buffer, this->total, sizeof(buffer)); RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer); - Meter_humanUnit(buffer, this->values[0], sizeof(buffer)); + Meter_humanUnit(buffer, this->values[ZRAM_METER_COMPRESSED], sizeof(buffer)); RichString_appendAscii(out, CRT_colors[METER_TEXT], " used:"); RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer); - Meter_humanUnit(buffer, this->values[1], sizeof(buffer)); + Meter_humanUnit(buffer, this->values[ZRAM_METER_UNCOMPRESSED], sizeof(buffer)); RichString_appendAscii(out, CRT_colors[METER_TEXT], " uncompressed:"); RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer); } @@ -64,7 +63,8 @@ const MeterClass ZramMeter_class = { }, .updateValues = ZramMeter_updateValues, .defaultMode = BAR_METERMODE, - .maxItems = 2, + .maxItems = ZRAM_METER_ITEMCOUNT, + .comprisedValues = true, .total = 100.0, .attributes = ZramMeter_attributes, .name = "Zram", diff --git a/linux/ZramMeter.h b/linux/ZramMeter.h index ddba1bae7..db27d0b35 100644 --- a/linux/ZramMeter.h +++ b/linux/ZramMeter.h @@ -3,6 +3,11 @@ #include "Meter.h" +typedef enum { + ZRAM_METER_COMPRESSED = 0, + ZRAM_METER_UNCOMPRESSED = 1, + ZRAM_METER_ITEMCOUNT = 2, // number of entries in this enum +} ZramMeterValues; extern const MeterClass ZramMeter_class; From 7a7c6938aad999f33ceb1e4ec5ebaada4c461f29 Mon Sep 17 00:00:00 2001 From: gandalf3 Date: Tue, 14 Mar 2023 12:55:54 -0700 Subject: [PATCH 077/106] Fix scroll relative to followed process --- ProcessList.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ProcessList.c b/ProcessList.c index 9d213d1d8..8e989d02f 100644 --- a/ProcessList.c +++ b/ProcessList.c @@ -413,7 +413,8 @@ void ProcessList_rebuildPanel(ProcessList* this) { if (this->following != -1 && p->pid == this->following) { foundFollowed = true; Panel_setSelected(this->panel, idx); - this->panel->scrollV = currScrollV; + /* Keep scroll position relative to followed process */ + this->panel->scrollV = idx - (currPos-currScrollV); } idx++; } From ed7eac5dfe8239f7942102f9d1a4f9ef5210f4d7 Mon Sep 17 00:00:00 2001 From: UeiWang Date: Sun, 16 Apr 2023 23:11:20 +0800 Subject: [PATCH 078/106] Darwin: add DiskIOMeter support --- configure.ac | 2 + darwin/Platform.c | 108 ++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 107 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac index a8b75dcde..36f25b22c 100644 --- a/configure.ac +++ b/configure.ac @@ -280,6 +280,8 @@ AC_CHECK_FUNCS([ \ if test "$my_htop_platform" = darwin; then AC_CHECK_FUNCS([mach_timebase_info]) + AC_CHECK_DECLS([IOMainPort], [], [], [[#include ]]) + AC_CHECK_DECLS([IOMasterPort], [], [], [[#include ]]) fi if test "$my_htop_platform" = pcp; then diff --git a/darwin/Platform.c b/darwin/Platform.c index 27bce5510..deba7abb2 100644 --- a/darwin/Platform.c +++ b/darwin/Platform.c @@ -14,10 +14,19 @@ in the source distribution for its full text. #include #include #include +#include + +#include +#include +#include #include #include + +#include +#include #include #include +#include #include "ClockMeter.h" #include "CPUMeter.h" @@ -128,6 +137,7 @@ const MeterClass* const Platform_meterTypes[] = { &RightCPUs8Meter_class, &ZfsArcMeter_class, &ZfsCompressedArcMeter_class, + &DiskIOMeter_class, &FileDescriptorMeter_class, &BlankMeter_class, NULL @@ -137,6 +147,9 @@ static double Platform_nanosecondsPerMachTick = 1.0; static double Platform_nanosecondsPerSchedulerTick = -1; +static bool iokit_available = false; +static mach_port_t iokit_port; // the mach port used to initiate communication with IOKit + bool Platform_init(void) { Platform_nanosecondsPerMachTick = Platform_calculateNanosecondsPerMachTick(); @@ -151,6 +164,17 @@ bool Platform_init(void) { const double nanos_per_sec = 1e9; Platform_nanosecondsPerSchedulerTick = nanos_per_sec / scheduler_ticks_per_sec; + // Since macOS 12.0, IOMasterPort is deprecated, and one should use IOMainPort instead + #if defined(HAVE_DECL_IOMAINPORT) && HAVE_DECL_IOMAINPORT + if (!IOMainPort(bootstrap_port, &iokit_port)) { + iokit_available = true; + } + #elif defined(HAVE_DECL_IOMASTERPORT) && HAVE_DECL_IOMASTERPORT + if (!IOMasterPort(bootstrap_port, &iokit_port)) { + iokit_available = true; + } + #endif + return true; } @@ -357,9 +381,87 @@ void Platform_getFileDescriptors(double* used, double* max) { } bool Platform_getDiskIO(DiskIOData* data) { - // TODO - (void)data; - return false; + if (!iokit_available) + return false; + + io_iterator_t drive_list; + + /* Get the list of all drives */ + if (IOServiceGetMatchingServices(iokit_port, IOServiceMatching("IOBlockStorageDriver"), &drive_list)) + return false; + + unsigned long long int read_sum = 0, write_sum = 0, timeSpend_sum = 0; + + io_registry_entry_t drive; + while ((drive = IOIteratorNext(drive_list)) != 0) { + CFMutableDictionaryRef properties_tmp = NULL; + + /* Get the properties of this drive */ + if (IORegistryEntryCreateCFProperties(drive, &properties_tmp, kCFAllocatorDefault, 0)) { + IOObjectRelease(drive); + IOObjectRelease(drive_list); + return false; + } + + if (!properties_tmp) { + IOObjectRelease(drive); + continue; + } + + CFDictionaryRef properties = properties_tmp; + + /* Get the statistics of this drive */ + CFDictionaryRef statistics = (CFDictionaryRef) CFDictionaryGetValue(properties, CFSTR(kIOBlockStorageDriverStatisticsKey)); + + if (!statistics) { + CFRelease(properties); + IOObjectRelease(drive); + continue; + } + + CFNumberRef number; + unsigned long long int value; + + /* Get bytes read */ + number = (CFNumberRef) CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsBytesReadKey)); + if (number != 0) { + CFNumberGetValue(number, kCFNumberSInt64Type, &value); + read_sum += value; + } + + /* Get bytes written */ + number = (CFNumberRef) CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsBytesWrittenKey)); + if (number != 0) { + CFNumberGetValue(number, kCFNumberSInt64Type, &value); + write_sum += value; + } + + /* Get total read time (in ns) */ + number = (CFNumberRef) CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsTotalReadTimeKey)); + if (number != 0) { + CFNumberGetValue(number, kCFNumberSInt64Type, &value); + timeSpend_sum += value; + } + + /* Get total write time (in ns) */ + number = (CFNumberRef) CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsTotalWriteTimeKey)); + if (number != 0) { + CFNumberGetValue(number, kCFNumberSInt64Type, &value); + timeSpend_sum += value; + } + + CFRelease(properties); + IOObjectRelease(drive); + } + + data->totalBytesRead = read_sum; + data->totalBytesWritten = write_sum; + data->totalMsTimeSpend = timeSpend_sum / 1e6; /* Convert from ns to ms */ + + if (drive_list) + IOObjectRelease(drive_list); + + return true; } bool Platform_getNetworkIO(NetworkIOData* data) { From 1b640dff5759d73458204c87e217dab8759badd9 Mon Sep 17 00:00:00 2001 From: Ivan Shapovalov Date: Sun, 12 Mar 2023 20:08:40 +0400 Subject: [PATCH 079/106] Compat: implement static_assert() shim for pre-C11 --- Compat.h | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/Compat.h b/Compat.h index 1c4794e65..b83fc3d80 100644 --- a/Compat.h +++ b/Compat.h @@ -9,6 +9,7 @@ in the source distribution for its full text. #include "config.h" // IWYU pragma: keep +#include // IWYU pragma: keep #include #include #include @@ -61,4 +62,26 @@ ssize_t Compat_readlink(openat_arg_t dirfd, char* buf, size_t bufsize); +/* + * static_assert() hack for pre-C11 + * TODO: drop after moving to -std=c11 or newer + */ + +/* C11 guarantees _Static_assert is a keyword */ +#if (defined __STDC_VERSION__ ? __STDC_VERSION__ : 0) < 201112L +# if !defined(_Static_assert) +# define _Static_assert(expr, msg) \ + extern int (*__Static_assert_function (void)) \ + [!!sizeof (struct { int __error_if_negative: (expr) ? 2 : -1; })] +# endif +#endif + +/* C23 will guarantee static_assert is a keyword or a macro */ +/* FIXME: replace 202300L with proper value once C23 is published */ +#if (defined __STDC_VERSION__ ? __STDC_VERSION__ : 0) < 202300L +# if !defined(static_assert) +# define static_assert(expr, msg) _Static_assert(expr, msg) +# endif +#endif + #endif /* HEADER_Compat */ From b2ada278403e643a3bc339783740f262b7879803 Mon Sep 17 00:00:00 2001 From: Ivan Shapovalov Date: Sun, 12 Mar 2023 20:17:03 +0400 Subject: [PATCH 080/106] {Memory,Swap}Meter: make sure new constants are used consistently --- MemoryMeter.c | 6 ++++-- SwapMeter.c | 6 +++--- linux/Platform.c | 4 ++-- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/MemoryMeter.c b/MemoryMeter.c index ac01dfe7d..1dad1356d 100644 --- a/MemoryMeter.c +++ b/MemoryMeter.c @@ -34,7 +34,9 @@ static void MemoryMeter_updateValues(Meter* this) { Platform_setMemoryValues(this); /* Do not print available memory in bar mode */ - this->curItems = 4; + static_assert(MEMORY_METER_AVAILABLE + 1 == MEMORY_METER_ITEMCOUNT, + "MEMORY_METER_AVAILABLE is not the last item in MemoryMeterValues"); + this->curItems = MEMORY_METER_AVAILABLE; written = Meter_humanUnit(buffer, this->values[MEMORY_METER_USED], size); METER_BUFFER_CHECK(buffer, size, written); @@ -87,7 +89,7 @@ const MeterClass MemoryMeter_class = { }, .updateValues = MemoryMeter_updateValues, .defaultMode = BAR_METERMODE, - .maxItems = 5, + .maxItems = MEMORY_METER_ITEMCOUNT, .total = 100.0, .attributes = MemoryMeter_attributes, .name = "Memory", diff --git a/SwapMeter.c b/SwapMeter.c index c0f482000..fea8e88b5 100644 --- a/SwapMeter.c +++ b/SwapMeter.c @@ -28,10 +28,10 @@ static void SwapMeter_updateValues(Meter* this) { size_t size = sizeof(this->txtBuffer); int written; - this->values[1] = NAN; /* 'cached' not present on all platforms */ + this->values[SWAP_METER_CACHE] = NAN; /* 'cached' not present on all platforms */ Platform_setSwapValues(this); - written = Meter_humanUnit(buffer, this->values[0], size); + written = Meter_humanUnit(buffer, this->values[SWAP_METER_USED], size); METER_BUFFER_CHECK(buffer, size, written); METER_BUFFER_APPEND_CHR(buffer, size, '/'); @@ -64,7 +64,7 @@ const MeterClass SwapMeter_class = { }, .updateValues = SwapMeter_updateValues, .defaultMode = BAR_METERMODE, - .maxItems = 2, + .maxItems = SWAP_METER_ITEMCOUNT, .total = 100.0, .attributes = SwapMeter_attributes, .name = "Swap", diff --git a/linux/Platform.c b/linux/Platform.c index e004b9b17..c14736bbf 100644 --- a/linux/Platform.c +++ b/linux/Platform.c @@ -376,8 +376,8 @@ void Platform_setMemoryValues(Meter* this) { void Platform_setSwapValues(Meter* this) { const ProcessList* pl = this->pl; this->total = pl->totalSwap; - this->values[0] = pl->usedSwap; - this->values[1] = pl->cachedSwap; + this->values[SWAP_METER_USED] = pl->usedSwap; + this->values[SWAP_METER_CACHE] = pl->cachedSwap; } void Platform_setZramValues(Meter* this) { From b29b33ebb9ae80a95e7d09eb537e0fccc5043d68 Mon Sep 17 00:00:00 2001 From: Ivan Shapovalov Date: Tue, 27 Dec 2022 04:57:52 +0400 Subject: [PATCH 081/106] {Memory,Swap}Meter: add "compressed memory" metrics For now, the semantics are mostly fit for Linux zswap subsystem. For instance, we add the third swap usage metric that indicates the amount of memory that is accounted to swap but in fact stored elsewhere. This exactly matches the definition of frontswap/zswap, and is probably of little use to all other platforms. --- Action.c | 6 ++++-- CRT.c | 12 ++++++++++++ CRT.h | 2 ++ MemoryMeter.c | 19 +++++++++++++++++-- MemoryMeter.h | 7 ++++--- SwapMeter.c | 10 +++++++++- SwapMeter.h | 3 ++- darwin/Platform.c | 3 +++ dragonflybsd/Platform.c | 4 +++- freebsd/Platform.c | 4 +++- linux/Platform.c | 1 + netbsd/Platform.c | 4 +++- openbsd/Platform.c | 4 +++- pcp/Platform.c | 2 ++ solaris/Platform.c | 4 +++- 15 files changed, 71 insertions(+), 14 deletions(-) diff --git a/Action.c b/Action.c index a67233cca..18c990e0b 100644 --- a/Action.c +++ b/Action.c @@ -674,10 +674,11 @@ static Htop_Reaction actionHelp(State* st) { mvaddstr(line++, 0, "Memory bar: "); addattrstr(CRT_colors[BAR_BORDER], "["); addbartext(CRT_colors[MEMORY_USED], "", "used"); + addbartext(CRT_colors[MEMORY_COMPRESSED], "/", "compressed"); addbartext(CRT_colors[MEMORY_BUFFERS_TEXT], "/", "buffers"); addbartext(CRT_colors[MEMORY_SHARED], "/", "shared"); addbartext(CRT_colors[MEMORY_CACHE], "/", "cache"); - addbartext(CRT_colors[BAR_SHADOW], " ", "used"); + addbartext(CRT_colors[BAR_SHADOW], " ", "used"); addbartext(CRT_colors[BAR_SHADOW], "/", "total"); addattrstr(CRT_colors[BAR_BORDER], "]"); @@ -687,10 +688,11 @@ static Htop_Reaction actionHelp(State* st) { addbartext(CRT_colors[SWAP], "", "used"); #ifdef HTOP_LINUX addbartext(CRT_colors[SWAP_CACHE], "/", "cache"); + addbartext(CRT_colors[SWAP_FRONTSWAP], "/", "frontswap"); #else addbartext(CRT_colors[SWAP_CACHE], " ", ""); #endif - addbartext(CRT_colors[BAR_SHADOW], " ", "used"); + addbartext(CRT_colors[BAR_SHADOW], " ", "used"); addbartext(CRT_colors[BAR_SHADOW], "/", "total"); addattrstr(CRT_colors[BAR_BORDER], "]"); diff --git a/CRT.c b/CRT.c index 85a381159..a82fc8c1b 100644 --- a/CRT.c +++ b/CRT.c @@ -163,6 +163,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [BAR_SHADOW] = A_BOLD | ColorPairGrayBlack, [SWAP] = ColorPair(Red, Black), [SWAP_CACHE] = ColorPair(Yellow, Black), + [SWAP_FRONTSWAP] = A_BOLD | ColorPairGrayBlack, [GRAPH_1] = A_BOLD | ColorPair(Cyan, Black), [GRAPH_2] = ColorPair(Cyan, Black), [MEMORY_USED] = ColorPair(Green, Black), @@ -170,6 +171,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [MEMORY_BUFFERS_TEXT] = A_BOLD | ColorPair(Blue, Black), [MEMORY_CACHE] = ColorPair(Yellow, Black), [MEMORY_SHARED] = ColorPair(Magenta, Black), + [MEMORY_COMPRESSED] = A_BOLD | ColorPairGrayBlack, [HUGEPAGE_1] = ColorPair(Green, Black), [HUGEPAGE_2] = ColorPair(Yellow, Black), [HUGEPAGE_3] = ColorPair(Red, Black), @@ -274,6 +276,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [BAR_SHADOW] = A_DIM, [SWAP] = A_BOLD, [SWAP_CACHE] = A_NORMAL, + [SWAP_FRONTSWAP] = A_DIM, [GRAPH_1] = A_BOLD, [GRAPH_2] = A_NORMAL, [MEMORY_USED] = A_BOLD, @@ -281,6 +284,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [MEMORY_BUFFERS_TEXT] = A_NORMAL, [MEMORY_CACHE] = A_NORMAL, [MEMORY_SHARED] = A_NORMAL, + [MEMORY_COMPRESSED] = A_DIM, [HUGEPAGE_1] = A_BOLD, [HUGEPAGE_2] = A_NORMAL, [HUGEPAGE_3] = A_REVERSE | A_BOLD, @@ -385,6 +389,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [BAR_SHADOW] = ColorPair(Black, White), [SWAP] = ColorPair(Red, White), [SWAP_CACHE] = ColorPair(Yellow, White), + [SWAP_FRONTSWAP] = A_BOLD | ColorPair(Black, White), [GRAPH_1] = A_BOLD | ColorPair(Blue, White), [GRAPH_2] = ColorPair(Blue, White), [MEMORY_USED] = ColorPair(Green, White), @@ -392,6 +397,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [MEMORY_BUFFERS_TEXT] = ColorPair(Cyan, White), [MEMORY_CACHE] = ColorPair(Yellow, White), [MEMORY_SHARED] = ColorPair(Magenta, White), + [MEMORY_COMPRESSED] = A_BOLD | ColorPair(Black, White), [HUGEPAGE_1] = ColorPair(Green, White), [HUGEPAGE_2] = ColorPair(Yellow, White), [HUGEPAGE_3] = ColorPair(Red, White), @@ -496,6 +502,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [BAR_SHADOW] = ColorPairGrayBlack, [SWAP] = ColorPair(Red, Black), [SWAP_CACHE] = ColorPair(Yellow, Black), + [SWAP_FRONTSWAP] = ColorPairGrayBlack, [GRAPH_1] = A_BOLD | ColorPair(Cyan, Black), [GRAPH_2] = ColorPair(Cyan, Black), [MEMORY_USED] = ColorPair(Green, Black), @@ -503,6 +510,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [MEMORY_BUFFERS_TEXT] = ColorPair(Cyan, Black), [MEMORY_CACHE] = ColorPair(Yellow, Black), [MEMORY_SHARED] = ColorPair(Magenta, Black), + [MEMORY_COMPRESSED] = ColorPairGrayBlack, [HUGEPAGE_1] = ColorPair(Green, Black), [HUGEPAGE_2] = ColorPair(Yellow, Black), [HUGEPAGE_3] = ColorPair(Red, Black), @@ -607,6 +615,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [BAR_SHADOW] = ColorPair(Cyan, Blue), [SWAP] = ColorPair(Red, Blue), [SWAP_CACHE] = A_BOLD | ColorPair(Yellow, Blue), + [SWAP_FRONTSWAP] = A_BOLD | ColorPair(Black, Blue), [GRAPH_1] = A_BOLD | ColorPair(Cyan, Blue), [GRAPH_2] = ColorPair(Cyan, Blue), [MEMORY_USED] = A_BOLD | ColorPair(Green, Blue), @@ -614,6 +623,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [MEMORY_BUFFERS_TEXT] = A_BOLD | ColorPair(Cyan, Blue), [MEMORY_CACHE] = A_BOLD | ColorPair(Yellow, Blue), [MEMORY_SHARED] = A_BOLD | ColorPair(Magenta, Blue), + [MEMORY_COMPRESSED] = A_BOLD | ColorPair(Black, Blue), [HUGEPAGE_1] = A_BOLD | ColorPair(Green, Blue), [HUGEPAGE_2] = A_BOLD | ColorPair(Yellow, Blue), [HUGEPAGE_3] = A_BOLD | ColorPair(Red, Blue), @@ -718,6 +728,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [BAR_SHADOW] = ColorPair(Cyan, Black), [SWAP] = ColorPair(Red, Black), [SWAP_CACHE] = ColorPair(Yellow, Black), + [SWAP_FRONTSWAP] = ColorPair(Yellow, Black), [GRAPH_1] = A_BOLD | ColorPair(Green, Black), [GRAPH_2] = ColorPair(Green, Black), [MEMORY_USED] = ColorPair(Green, Black), @@ -725,6 +736,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [MEMORY_BUFFERS_TEXT] = A_BOLD | ColorPair(Blue, Black), [MEMORY_CACHE] = ColorPair(Yellow, Black), [MEMORY_SHARED] = ColorPair(Magenta, Black), + [MEMORY_COMPRESSED] = ColorPair(Yellow, Black), [HUGEPAGE_1] = ColorPair(Green, Black), [HUGEPAGE_2] = ColorPair(Yellow, Black), [HUGEPAGE_3] = ColorPair(Red, Black), diff --git a/CRT.h b/CRT.h index 278c1e815..ae78c9c50 100644 --- a/CRT.h +++ b/CRT.h @@ -68,6 +68,7 @@ typedef enum ColorElements_ { TASKS_RUNNING, SWAP, SWAP_CACHE, + SWAP_FRONTSWAP, PROCESS, PROCESS_SHADOW, PROCESS_TAG, @@ -95,6 +96,7 @@ typedef enum ColorElements_ { MEMORY_BUFFERS_TEXT, MEMORY_CACHE, MEMORY_SHARED, + MEMORY_COMPRESSED, HUGEPAGE_1, HUGEPAGE_2, HUGEPAGE_3, diff --git a/MemoryMeter.c b/MemoryMeter.c index 1dad1356d..28c0be277 100644 --- a/MemoryMeter.c +++ b/MemoryMeter.c @@ -20,6 +20,7 @@ static const int MemoryMeter_attributes[] = { MEMORY_USED, MEMORY_BUFFERS, MEMORY_SHARED, + MEMORY_COMPRESSED, MEMORY_CACHE }; @@ -28,8 +29,9 @@ static void MemoryMeter_updateValues(Meter* this) { size_t size = sizeof(this->txtBuffer); int written; - /* shared and available memory are not supported on all platforms */ + /* shared, compressed and available memory are not supported on all platforms */ this->values[MEMORY_METER_SHARED] = NAN; + this->values[MEMORY_METER_COMPRESSED] = NAN; this->values[MEMORY_METER_AVAILABLE] = NAN; Platform_setMemoryValues(this); @@ -38,7 +40,13 @@ static void MemoryMeter_updateValues(Meter* this) { "MEMORY_METER_AVAILABLE is not the last item in MemoryMeterValues"); this->curItems = MEMORY_METER_AVAILABLE; - written = Meter_humanUnit(buffer, this->values[MEMORY_METER_USED], size); + /* we actually want to show "used + compressed" */ + double used = this->values[MEMORY_METER_USED]; + if (!isnan(this->values[MEMORY_METER_COMPRESSED])) { + used += this->values[MEMORY_METER_COMPRESSED]; + } + + written = Meter_humanUnit(buffer, used, size); METER_BUFFER_CHECK(buffer, size, written); METER_BUFFER_APPEND_CHR(buffer, size, '/'); @@ -69,6 +77,13 @@ static void MemoryMeter_display(const Object* cast, RichString* out) { RichString_appendAscii(out, CRT_colors[MEMORY_SHARED], buffer); } + /* compressed memory is not supported on all platforms */ + if (!isnan(this->values[MEMORY_METER_COMPRESSED])) { + Meter_humanUnit(buffer, this->values[MEMORY_METER_COMPRESSED], sizeof(buffer)); + RichString_appendAscii(out, CRT_colors[METER_TEXT], " compressed:"); + RichString_appendAscii(out, CRT_colors[MEMORY_COMPRESSED], buffer); + } + Meter_humanUnit(buffer, this->values[MEMORY_METER_CACHE], sizeof(buffer)); RichString_appendAscii(out, CRT_colors[METER_TEXT], " cache:"); RichString_appendAscii(out, CRT_colors[MEMORY_CACHE], buffer); diff --git a/MemoryMeter.h b/MemoryMeter.h index d8f5fdbc9..b6568afca 100644 --- a/MemoryMeter.h +++ b/MemoryMeter.h @@ -13,9 +13,10 @@ typedef enum { MEMORY_METER_USED = 0, MEMORY_METER_BUFFERS = 1, MEMORY_METER_SHARED = 2, - MEMORY_METER_CACHE = 3, - MEMORY_METER_AVAILABLE = 4, - MEMORY_METER_ITEMCOUNT = 5, // number of entries in this enum + MEMORY_METER_COMPRESSED = 3, + MEMORY_METER_CACHE = 4, + MEMORY_METER_AVAILABLE = 5, + MEMORY_METER_ITEMCOUNT = 6, // number of entries in this enum } MemoryMeterValues; extern const MeterClass MemoryMeter_class; diff --git a/SwapMeter.c b/SwapMeter.c index fea8e88b5..84e58a26e 100644 --- a/SwapMeter.c +++ b/SwapMeter.c @@ -20,7 +20,8 @@ in the source distribution for its full text. static const int SwapMeter_attributes[] = { SWAP, - SWAP_CACHE + SWAP_CACHE, + SWAP_FRONTSWAP, }; static void SwapMeter_updateValues(Meter* this) { @@ -29,6 +30,7 @@ static void SwapMeter_updateValues(Meter* this) { int written; this->values[SWAP_METER_CACHE] = NAN; /* 'cached' not present on all platforms */ + this->values[SWAP_METER_FRONTSWAP] = NAN; /* 'frontswap' not present on all platforms */ Platform_setSwapValues(this); written = Meter_humanUnit(buffer, this->values[SWAP_METER_USED], size); @@ -54,6 +56,12 @@ static void SwapMeter_display(const Object* cast, RichString* out) { RichString_appendAscii(out, CRT_colors[METER_TEXT], " cache:"); RichString_appendAscii(out, CRT_colors[SWAP_CACHE], buffer); } + + if (!isnan(this->values[SWAP_METER_FRONTSWAP])) { + Meter_humanUnit(buffer, this->values[SWAP_METER_FRONTSWAP], sizeof(buffer)); + RichString_appendAscii(out, CRT_colors[METER_TEXT], " frontswap:"); + RichString_appendAscii(out, CRT_colors[SWAP_FRONTSWAP], buffer); + } } const MeterClass SwapMeter_class = { diff --git a/SwapMeter.h b/SwapMeter.h index 162897fb7..94b8dc859 100644 --- a/SwapMeter.h +++ b/SwapMeter.h @@ -12,7 +12,8 @@ in the source distribution for its full text. typedef enum { SWAP_METER_USED = 0, SWAP_METER_CACHE = 1, - SWAP_METER_ITEMCOUNT = 2, // number of entries in this enum + SWAP_METER_FRONTSWAP = 2, + SWAP_METER_ITEMCOUNT = 3, // number of entries in this enum } SwapMeterValues; extern const MeterClass SwapMeter_class; diff --git a/darwin/Platform.c b/darwin/Platform.c index deba7abb2..20bfec26e 100644 --- a/darwin/Platform.c +++ b/darwin/Platform.c @@ -294,6 +294,7 @@ void Platform_setMemoryValues(Meter* mtr) { mtr->values[MEMORY_METER_USED] = (double)(vm->active_count + vm->wire_count) * page_K; mtr->values[MEMORY_METER_BUFFERS] = (double)vm->purgeable_count * page_K; // mtr->values[MEMORY_METER_SHARED] = "shared memory, like tmpfs and shm" + // mtr->values[MEMORY_METER_COMPRESSED] = "compressed memory, like zswap on linux" mtr->values[MEMORY_METER_CACHE] = (double)vm->inactive_count * page_K; // mtr->values[MEMORY_METER_AVAILABLE] = "available memory" } @@ -306,6 +307,8 @@ void Platform_setSwapValues(Meter* mtr) { mtr->total = swapused.xsu_total / 1024; mtr->values[SWAP_METER_USED] = swapused.xsu_used / 1024; + // mtr->values[SWAP_METER_CACHE] = "pages that are both in swap and RAM, like SwapCached on linux" + // mtr->values[SWAP_METER_FRONTSWAP] = "pages that are accounted to swap but stored elsewhere, like frontswap on linux" } void Platform_setZfsArcValues(Meter* this) { diff --git a/dragonflybsd/Platform.c b/dragonflybsd/Platform.c index ab21d3650..995276421 100644 --- a/dragonflybsd/Platform.c +++ b/dragonflybsd/Platform.c @@ -215,6 +215,7 @@ void Platform_setMemoryValues(Meter* this) { this->values[MEMORY_METER_USED] = pl->usedMem; this->values[MEMORY_METER_BUFFERS] = pl->buffersMem; // this->values[MEMORY_METER_SHARED] = "shared memory, like tmpfs and shm" + // mtr->values[MEMORY_METER_COMPRESSED] = "compressed memory, like zswap on linux" this->values[MEMORY_METER_CACHE] = pl->cachedMem; // this->values[MEMORY_METER_AVAILABLE] = "available memory" } @@ -223,7 +224,8 @@ void Platform_setSwapValues(Meter* this) { const ProcessList* pl = this->pl; this->total = pl->totalSwap; this->values[SWAP_METER_USED] = pl->usedSwap; - this->values[SWAP_METER_CACHE] = NAN; + // mtr->values[SWAP_METER_CACHE] = "pages that are both in swap and RAM, like SwapCached on linux" + // mtr->values[SWAP_METER_FRONTSWAP] = "pages that are accounted to swap but stored elsewhere, like frontswap on linux" } char* Platform_getProcessEnv(pid_t pid) { diff --git a/freebsd/Platform.c b/freebsd/Platform.c index 618ed6b42..cab80929b 100644 --- a/freebsd/Platform.c +++ b/freebsd/Platform.c @@ -236,6 +236,7 @@ void Platform_setMemoryValues(Meter* this) { this->values[MEMORY_METER_USED] = pl->usedMem; this->values[MEMORY_METER_BUFFERS] = pl->buffersMem; this->values[MEMORY_METER_SHARED] = pl->sharedMem; + // this->values[MEMORY_METER_COMPRESSED] = "compressed memory, like zswap on linux" this->values[MEMORY_METER_CACHE] = pl->cachedMem; // this->values[MEMORY_METER_AVAILABLE] = "available memory" @@ -254,7 +255,8 @@ void Platform_setSwapValues(Meter* this) { const ProcessList* pl = this->pl; this->total = pl->totalSwap; this->values[SWAP_METER_USED] = pl->usedSwap; - this->values[SWAP_METER_CACHE] = NAN; + // this->values[SWAP_METER_CACHE] = "pages that are both in swap and RAM, like SwapCached on linux" + // this->values[SWAP_METER_FRONTSWAP] = "pages that are accounted to swap but stored elsewhere, like frontswap on linux" } void Platform_setZfsArcValues(Meter* this) { diff --git a/linux/Platform.c b/linux/Platform.c index c14736bbf..45fee3927 100644 --- a/linux/Platform.c +++ b/linux/Platform.c @@ -359,6 +359,7 @@ void Platform_setMemoryValues(Meter* this) { this->values[MEMORY_METER_USED] = pl->usedMem; this->values[MEMORY_METER_BUFFERS] = pl->buffersMem; this->values[MEMORY_METER_SHARED] = pl->sharedMem; + // this->values[MEMORY_METER_COMPRESSED] = "compressed memory, like zswap on linux" this->values[MEMORY_METER_CACHE] = pl->cachedMem; this->values[MEMORY_METER_AVAILABLE] = pl->availableMem; diff --git a/netbsd/Platform.c b/netbsd/Platform.c index 9c9546fec..1cf160670 100644 --- a/netbsd/Platform.c +++ b/netbsd/Platform.c @@ -274,6 +274,7 @@ void Platform_setMemoryValues(Meter* this) { this->values[MEMORY_METER_USED] = pl->usedMem; this->values[MEMORY_METER_BUFFERS] = pl->buffersMem; // this->values[MEMORY_METER_SHARED] = "shared memory, like tmpfs and shm" + // this->values[MEMORY_METER_COMPRESSED] = "compressed memory, like zswap on linux" this->values[MEMORY_METER_CACHE] = pl->cachedMem; // this->values[MEMORY_METER_AVAILABLE] = "available memory" } @@ -282,7 +283,8 @@ void Platform_setSwapValues(Meter* this) { const ProcessList* pl = this->pl; this->total = pl->totalSwap; this->values[SWAP_METER_USED] = pl->usedSwap; - this->values[SWAP_METER_CACHE] = NAN; + // this->values[SWAP_METER_CACHE] = "pages that are both in swap and RAM, like SwapCached on linux" + // this->values[SWAP_METER_FRONTSWAP] = "pages that are accounted to swap but stored elsewhere, like frontswap on linux" } char* Platform_getProcessEnv(pid_t pid) { diff --git a/openbsd/Platform.c b/openbsd/Platform.c index e49a0f817..707d21068 100644 --- a/openbsd/Platform.c +++ b/openbsd/Platform.c @@ -231,6 +231,7 @@ void Platform_setMemoryValues(Meter* this) { this->values[MEMORY_METER_USED] = usedMem; this->values[MEMORY_METER_BUFFERS] = buffersMem; // this->values[MEMORY_METER_SHARED] = "shared memory, like tmpfs and shm" + // this->values[MEMORY_METER_COMPRESSED] = "compressed memory, like zswap on linux" this->values[MEMORY_METER_CACHE] = cachedMem; // this->values[MEMORY_METER_AVAILABLE] = "available memory" } @@ -239,7 +240,8 @@ void Platform_setSwapValues(Meter* this) { const ProcessList* pl = this->pl; this->total = pl->totalSwap; this->values[SWAP_METER_USED] = pl->usedSwap; - this->values[SWAP_METER_CACHE] = NAN; + // this->values[SWAP_METER_CACHE] = "pages that are both in swap and RAM, like SwapCached on linux" + // this->values[SWAP_METER_FRONTSWAP] = "pages that are accounted to swap but stored elsewhere, like frontswap on linux" } char* Platform_getProcessEnv(pid_t pid) { diff --git a/pcp/Platform.c b/pcp/Platform.c index 22963dda3..87de2c2f7 100644 --- a/pcp/Platform.c +++ b/pcp/Platform.c @@ -537,6 +537,7 @@ void Platform_setMemoryValues(Meter* this) { this->values[MEMORY_METER_USED] = pl->usedMem; this->values[MEMORY_METER_BUFFERS] = pl->buffersMem; this->values[MEMORY_METER_SHARED] = pl->sharedMem; + // this->values[MEMORY_METER_COMPRESSED] = "compressed memory, like zswap on linux" this->values[MEMORY_METER_CACHE] = pl->cachedMem; this->values[MEMORY_METER_AVAILABLE] = pl->availableMem; @@ -556,6 +557,7 @@ void Platform_setSwapValues(Meter* this) { this->total = pl->totalSwap; this->values[SWAP_METER_USED] = pl->usedSwap; this->values[SWAP_METER_CACHE] = pl->cachedSwap; + // this->values[SWAP_METER_FRONTSWAP] = "pages that are accounted to swap but stored elsewhere, like frontswap on linux" } void Platform_setZramValues(Meter* this) { diff --git a/solaris/Platform.c b/solaris/Platform.c index f7ab65585..79b6a9ead 100644 --- a/solaris/Platform.c +++ b/solaris/Platform.c @@ -240,6 +240,7 @@ void Platform_setMemoryValues(Meter* this) { this->values[MEMORY_METER_USED] = pl->usedMem; this->values[MEMORY_METER_BUFFERS] = pl->buffersMem; // this->values[MEMORY_METER_SHARED] = "shared memory, like tmpfs and shm" + // this->values[MEMORY_METER_COMPRESSED] = "compressed memory, like zswap on linux" this->values[MEMORY_METER_CACHE] = pl->cachedMem; // this->values[MEMORY_METER_AVAILABLE] = "available memory" } @@ -248,7 +249,8 @@ void Platform_setSwapValues(Meter* this) { const ProcessList* pl = this->pl; this->total = pl->totalSwap; this->values[SWAP_METER_USED] = pl->usedSwap; - this->values[SWAP_METER_CACHE] = NAN; + // this->values[SWAP_METER_CACHE] = "pages that are both in swap and RAM, like SwapCached on linux" + // this->values[SWAP_METER_FRONTSWAP] = "pages that are accounted to swap but stored elsewhere, like frontswap on linux" } void Platform_setZfsArcValues(Meter* this) { From 0c8df5f9939cfed393a3740a77ef588ad636bb87 Mon Sep 17 00:00:00 2001 From: Ivan Shapovalov Date: Sun, 23 Apr 2023 01:07:19 +0300 Subject: [PATCH 082/106] Linux: fix include style issues for ZramStats.h --- linux/LinuxProcessList.h | 2 +- linux/ZramStats.h | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/linux/LinuxProcessList.h b/linux/LinuxProcessList.h index 1b29b3bde..b6c015e31 100644 --- a/linux/LinuxProcessList.h +++ b/linux/LinuxProcessList.h @@ -15,7 +15,7 @@ in the source distribution for its full text. #include "Hashtable.h" #include "ProcessList.h" #include "UsersTable.h" -#include "ZramStats.h" +#include "linux/ZramStats.h" #include "zfs/ZfsArcStats.h" #define HTOP_HUGEPAGE_BASE_SHIFT 16 diff --git a/linux/ZramStats.h b/linux/ZramStats.h index 67aadcc52..1c1625a42 100644 --- a/linux/ZramStats.h +++ b/linux/ZramStats.h @@ -1,5 +1,13 @@ #ifndef HEADER_ZramStats #define HEADER_ZramStats +/* +htop - ZramStats.h +(C) 2020 htop dev team +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "ProcessList.h" typedef struct ZramStats_ { memory_t totalZram; From 71f5a80d9e312375eff28d2fbb7d8add81f6793c Mon Sep 17 00:00:00 2001 From: Ivan Shapovalov Date: Tue, 27 Dec 2022 05:16:54 +0400 Subject: [PATCH 083/106] Linux: implement zswap support On Linux, use zswap to populate "compressed memory" metrics added in the previous commit. Fixes #104. --- Makefile.am | 1 + linux/LinuxProcessList.c | 28 ++++++++++++++++++++++++++++ linux/LinuxProcessList.h | 2 ++ linux/Platform.c | 15 ++++++++++++++- linux/ZswapStats.h | 21 +++++++++++++++++++++ 5 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 linux/ZswapStats.h diff --git a/Makefile.am b/Makefile.am index 2a6db44d4..427342014 100644 --- a/Makefile.am +++ b/Makefile.am @@ -173,6 +173,7 @@ linux_platform_headers = \ linux/SystemdMeter.h \ linux/ZramMeter.h \ linux/ZramStats.h \ + linux/ZswapStats.h \ zfs/ZfsArcMeter.h \ zfs/ZfsArcStats.h \ zfs/ZfsCompressedArcMeter.h diff --git a/linux/LinuxProcessList.c b/linux/LinuxProcessList.c index a3472c535..2ecdefc34 100644 --- a/linux/LinuxProcessList.c +++ b/linux/LinuxProcessList.c @@ -1733,6 +1733,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ } static inline void LinuxProcessList_scanMemoryInfo(ProcessList* this) { + LinuxProcessList *lpl = (LinuxProcessList *)this; memory_t availableMem = 0; memory_t freeMem = 0; memory_t totalMem = 0; @@ -1743,6 +1744,8 @@ static inline void LinuxProcessList_scanMemoryInfo(ProcessList* this) { memory_t swapCacheMem = 0; memory_t swapFreeMem = 0; memory_t sreclaimableMem = 0; + memory_t zswapCompMem = 0; + memory_t zswapOrigMem = 0; FILE* file = fopen(PROCMEMINFOFILE, "r"); if (!file) @@ -1787,6 +1790,10 @@ static inline void LinuxProcessList_scanMemoryInfo(ProcessList* this) { break; } break; + case 'Z': + tryRead("Zswap:", zswapCompMem); + tryRead("Zswapped:", zswapOrigMem); + break; } #undef tryRead @@ -1812,6 +1819,8 @@ static inline void LinuxProcessList_scanMemoryInfo(ProcessList* this) { this->totalSwap = swapTotalMem; this->usedSwap = swapTotalMem - swapFreeMem - swapCacheMem; this->cachedSwap = swapCacheMem; + lpl->zswap.usedZswapComp = zswapCompMem; + lpl->zswap.usedZswapOrig = zswapOrigMem; } static void LinuxProcessList_scanHugePages(LinuxProcessList* this) { @@ -1870,6 +1879,24 @@ static void LinuxProcessList_scanHugePages(LinuxProcessList* this) { closedir(dir); } +static inline void LinuxProcessList_scanZswapInfo(LinuxProcessList *this) { + long max_pool_percent = 0; + int r; + char buf[256]; + + r = xReadfile("/sys/module/zswap/parameters/max_pool_percent", buf, 256); + if (r <= 0) { + return; + } + max_pool_percent = strtol(buf, NULL, 10); + if (max_pool_percent < 0 || max_pool_percent > 100) { + return; + } + + this->zswap.totalZswapPool = this->super.totalMem * max_pool_percent / 100; + /* the rest of the metrics are set in LinuxProcessList_scanMemoryInfo() */ +} + static inline void LinuxProcessList_scanZramInfo(LinuxProcessList* this) { memory_t totalZram = 0; memory_t usedZramComp = 0; @@ -2222,6 +2249,7 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { LinuxProcessList_scanHugePages(this); LinuxProcessList_scanZfsArcstats(this); LinuxProcessList_scanZramInfo(this); + LinuxProcessList_scanZswapInfo(this); double period = LinuxProcessList_scanCPUTime(super); diff --git a/linux/LinuxProcessList.h b/linux/LinuxProcessList.h index b6c015e31..9cfdf24aa 100644 --- a/linux/LinuxProcessList.h +++ b/linux/LinuxProcessList.h @@ -16,6 +16,7 @@ in the source distribution for its full text. #include "ProcessList.h" #include "UsersTable.h" #include "linux/ZramStats.h" +#include "linux/ZswapStats.h" #include "zfs/ZfsArcStats.h" #define HTOP_HUGEPAGE_BASE_SHIFT 16 @@ -85,6 +86,7 @@ typedef struct LinuxProcessList_ { ZfsArcStats zfs; ZramStats zram; + ZswapStats zswap; } LinuxProcessList; #ifndef PROCDIR diff --git a/linux/Platform.c b/linux/Platform.c index 45fee3927..8015f55b8 100644 --- a/linux/Platform.c +++ b/linux/Platform.c @@ -359,7 +359,7 @@ void Platform_setMemoryValues(Meter* this) { this->values[MEMORY_METER_USED] = pl->usedMem; this->values[MEMORY_METER_BUFFERS] = pl->buffersMem; this->values[MEMORY_METER_SHARED] = pl->sharedMem; - // this->values[MEMORY_METER_COMPRESSED] = "compressed memory, like zswap on linux" + this->values[MEMORY_METER_COMPRESSED] = 0; /* compressed */ this->values[MEMORY_METER_CACHE] = pl->cachedMem; this->values[MEMORY_METER_AVAILABLE] = pl->availableMem; @@ -372,13 +372,26 @@ void Platform_setMemoryValues(Meter* this) { this->values[MEMORY_METER_CACHE] += shrinkableSize; this->values[MEMORY_METER_AVAILABLE] += shrinkableSize; } + + if (lpl->zswap.usedZswapOrig > 0 || lpl->zswap.usedZswapComp > 0) { + this->values[MEMORY_METER_USED] -= lpl->zswap.usedZswapComp; + this->values[MEMORY_METER_COMPRESSED] += lpl->zswap.usedZswapComp; + } } void Platform_setSwapValues(Meter* this) { const ProcessList* pl = this->pl; + const LinuxProcessList* lpl = (const LinuxProcessList*) pl; + this->total = pl->totalSwap; this->values[SWAP_METER_USED] = pl->usedSwap; this->values[SWAP_METER_CACHE] = pl->cachedSwap; + this->values[SWAP_METER_FRONTSWAP] = 0; /* frontswap -- memory that is accounted to swap but resides elsewhere */ + + if (lpl->zswap.usedZswapOrig > 0 || lpl->zswap.usedZswapComp > 0) { + this->values[SWAP_METER_USED] -= lpl->zswap.usedZswapOrig; + this->values[SWAP_METER_FRONTSWAP] += lpl->zswap.usedZswapOrig; + } } void Platform_setZramValues(Meter* this) { diff --git a/linux/ZswapStats.h b/linux/ZswapStats.h new file mode 100644 index 000000000..78771e101 --- /dev/null +++ b/linux/ZswapStats.h @@ -0,0 +1,21 @@ +#ifndef HEADER_ZswapStats +#define HEADER_ZswapStats +/* +htop - ZswapStats.h +(C) 2022 htop dev team +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "ProcessList.h" + +typedef struct ZswapStats_ { + /* maximum size of the zswap pool */ + memory_t totalZswapPool; + /* amount of RAM used by the zswap pool */ + memory_t usedZswapComp; + /* amount of data stored inside the zswap pool */ + memory_t usedZswapOrig; +} ZswapStats; + +#endif From 71f2e66126286540ca88d8c36f81008ba5181f89 Mon Sep 17 00:00:00 2001 From: Ivan Shapovalov Date: Tue, 27 Dec 2022 17:39:05 +0400 Subject: [PATCH 084/106] Linux: (hack) work around the fact that Zswapped pages may be SwapCached --- linux/Platform.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/linux/Platform.c b/linux/Platform.c index 8015f55b8..782f15461 100644 --- a/linux/Platform.c +++ b/linux/Platform.c @@ -389,7 +389,22 @@ void Platform_setSwapValues(Meter* this) { this->values[SWAP_METER_FRONTSWAP] = 0; /* frontswap -- memory that is accounted to swap but resides elsewhere */ if (lpl->zswap.usedZswapOrig > 0 || lpl->zswap.usedZswapComp > 0) { + /* + * FIXME: Zswapped pages can be both SwapUsed and SwapCached, and we do not know which. + * + * Apparently, it is possible that Zswapped > SwapUsed. This means that some of Zswapped pages + * were actually SwapCached, nor SwapUsed. Unfortunately, we cannot tell what exactly portion + * of Zswapped pages were SwapCached. + * + * For now, subtract Zswapped from SwapUsed and only if Zswapped > SwapUsed, subtract the + * overflow from SwapCached. + */ this->values[SWAP_METER_USED] -= lpl->zswap.usedZswapOrig; + if (this->values[SWAP_METER_USED] < 0) { + /* subtract the overflow from SwapCached */ + this->values[SWAP_METER_CACHE] += this->values[SWAP_METER_USED]; + this->values[SWAP_METER_USED] = 0; + } this->values[SWAP_METER_FRONTSWAP] += lpl->zswap.usedZswapOrig; } } From f50944c3d563238c9843057978041f2cd7c3b285 Mon Sep 17 00:00:00 2001 From: Benny Baumann Date: Wed, 26 Apr 2023 12:19:04 +0200 Subject: [PATCH 085/106] Force re-sorting of the process list view after switching between list/treeview mode Fixes: #1225 --- Action.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Action.c b/Action.c index 18c990e0b..c5b16360c 100644 --- a/Action.c +++ b/Action.c @@ -253,6 +253,9 @@ static Htop_Reaction actionToggleTreeView(State* st) { if (!ss->allBranchesCollapsed) ProcessList_expandTree(st->pl); + + st->pl->needsSort = true; + return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR; } From 8a8df71f6f355fdaf748eb80bf8558c1bcbec937 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 5 May 2023 11:28:41 +0200 Subject: [PATCH 086/106] Fix typo in code comment --- darwin/DarwinProcessList.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/darwin/DarwinProcessList.c b/darwin/DarwinProcessList.c index f7f1e592a..6e9eecf7c 100644 --- a/darwin/DarwinProcessList.c +++ b/darwin/DarwinProcessList.c @@ -159,7 +159,7 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { /* We use kinfo_procs for initial data since : * * 1) They always succeed. - * 2) The contain the basic information. + * 2) They contain the basic information. * * We attempt to fill-in additional information with libproc. */ From e4ebe18b67c366d367231a1123b057c82018cf5b Mon Sep 17 00:00:00 2001 From: Nathan Scott Date: Tue, 14 Feb 2023 11:51:54 +1100 Subject: [PATCH 087/106] Drop references to 'Process' in generic screen code Prepare the way for making screen tabs more generalised, able to cater to entities other than processes. 'actionTogglePauseProcessUpdate' -> 'actionTogglePauseUpdate' 'pauseProcessUpdate' -> 'pauseUpdate' 'hideProcessSelection' -> 'hideSelection' 'hideProcessSelection' -> 'hideSelection' Signed-off-by: Sohaib Mohamed --- Action.c | 6 +++--- Action.h | 6 +++--- CommandLine.c | 4 ++-- MainPanel.c | 6 +++--- ScreenManager.c | 6 +++--- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Action.c b/Action.c index c5b16360c..73b86062c 100644 --- a/Action.c +++ b/Action.c @@ -568,8 +568,8 @@ static Htop_Reaction actionRedraw(ATTR_UNUSED State* st) { return HTOP_REFRESH | HTOP_REDRAW_BAR; } -static Htop_Reaction actionTogglePauseProcessUpdate(State* st) { - st->pauseProcessUpdate = !st->pauseProcessUpdate; +static Htop_Reaction actionTogglePauseUpdate(State* st) { + st->pauseUpdate = !st->pauseUpdate; return HTOP_REFRESH | HTOP_REDRAW_BAR; } @@ -836,7 +836,7 @@ void Action_setBindings(Htop_Action* keys) { #ifdef SCHEDULER_SUPPORT keys['Y'] = actionSetSchedPolicy; #endif - keys['Z'] = actionTogglePauseProcessUpdate; + keys['Z'] = actionTogglePauseUpdate; keys['['] = actionLowerPriority; keys['\014'] = actionRedraw; // Ctrl+L keys['\177'] = actionCollapseIntoParent; diff --git a/Action.h b/Action.h index 09b68bd72..04d090f8f 100644 --- a/Action.h +++ b/Action.h @@ -41,13 +41,13 @@ typedef struct State_ { ProcessList* pl; struct MainPanel_* mainPanel; Header* header; - bool pauseProcessUpdate; - bool hideProcessSelection; + bool pauseUpdate; + bool hideSelection; bool hideMeters; } State; static inline bool State_hideFunctionBar(const State* st) { - return st->settings->hideFunctionBar == 2 || (st->settings->hideFunctionBar == 1 && st->hideProcessSelection); + return st->settings->hideFunctionBar == 2 || (st->settings->hideFunctionBar == 1 && st->hideSelection); } typedef Htop_Reaction (*Htop_Action)(State* st); diff --git a/CommandLine.c b/CommandLine.c index 2b2a68c2a..9dd02dfc9 100644 --- a/CommandLine.c +++ b/CommandLine.c @@ -372,8 +372,8 @@ int CommandLine_run(int argc, char** argv) { .pl = pl, .mainPanel = panel, .header = header, - .pauseProcessUpdate = false, - .hideProcessSelection = false, + .pauseUpdate = false, + .hideSelection = false, .hideMeters = false, }; diff --git a/MainPanel.c b/MainPanel.c index 89b4e7d41..0889a5c20 100644 --- a/MainPanel.c +++ b/MainPanel.c @@ -70,7 +70,7 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) { needReset = false; #endif if (needReset) - this->state->hideProcessSelection = false; + this->state->hideSelection = false; Settings* settings = this->state->settings; ScreenSettings* ss = settings->ss; @@ -107,7 +107,7 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) { } result = HANDLED; } else if (ch == 27) { - this->state->hideProcessSelection = true; + this->state->hideSelection = true; return HANDLED; } else if (ch != ERR && ch > 0 && ch < KEY_MAX && this->keys[ch]) { reaction |= (this->keys[ch])(this->state); @@ -190,7 +190,7 @@ static void MainPanel_drawFunctionBar(Panel* super, bool hideFunctionBar) { return; IncSet_drawBar(this->inc, CRT_colors[FUNCTION_BAR]); - if (this->state->pauseProcessUpdate) { + if (this->state->pauseUpdate) { FunctionBar_append("PAUSED", CRT_colors[PAUSED]); } } diff --git a/ScreenManager.c b/ScreenManager.c index 87e718926..6b9fada51 100644 --- a/ScreenManager.c +++ b/ScreenManager.c @@ -131,12 +131,12 @@ static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTi if (*rescan) { *oldTime = newTime; int oldUidDigits = Process_uidDigits; - if (!this->state->pauseProcessUpdate && (*sortTimeout == 0 || this->settings->ss->treeView)) { + if (!this->state->pauseUpdate && (*sortTimeout == 0 || this->settings->ss->treeView)) { pl->needsSort = true; *sortTimeout = 1; } // scan processes first - some header values are calculated there - ProcessList_scan(pl, this->state->pauseProcessUpdate); + ProcessList_scan(pl, this->state->pauseUpdate); // always update header, especially to avoid gaps in graph meters Header_updateData(this->header); // force redraw if the number of UID digits was changed @@ -206,7 +206,7 @@ static void ScreenManager_drawPanels(ScreenManager* this, int focus, bool force_ Panel_draw(panel, force_redraw, i == focus, - panel != (Panel*)this->state->mainPanel || !this->state->hideProcessSelection, + panel != (Panel*)this->state->mainPanel || !this->state->hideSelection, State_hideFunctionBar(this->state)); mvvline(panel->y, panel->x + panel->w, ' ', panel->h + (State_hideFunctionBar(this->state) ? 1 : 0)); } From 0bdade1b6cb40c5bd374a93ac0489058a7421bb5 Mon Sep 17 00:00:00 2001 From: Nathan Scott Date: Tue, 2 May 2023 09:02:22 +1000 Subject: [PATCH 088/106] Introduce Machine class for host-specific info (split from ProcessList) First stage in sanitizing the process list structure so that htop can support other types of lists too (cgroups, filesystems, ...), in the not-too-distant future. This introduces struct Machine for system-wide information while keeping process-list information in ProcessList (now much less). Next step is to propogate this separation into each platform, to match these core changes. --- Action.c | 111 ++++++++++++++----------- Action.h | 11 ++- Affinity.c | 20 ++--- Affinity.h | 10 +-- AffinityPanel.c | 24 +++--- AffinityPanel.h | 6 +- AvailableMetersPanel.c | 21 ++--- AvailableMetersPanel.h | 8 +- CPUMeter.c | 34 ++++---- CategoriesPanel.c | 24 +++--- CategoriesPanel.h | 9 +- ClockMeter.c | 4 +- CommandLine.c | 26 +++--- DateMeter.c | 4 +- DateTimeMeter.c | 4 +- DiskIOMeter.c | 6 +- DynamicMeter.c | 4 +- Header.c | 24 +++--- Header.h | 8 +- LoadAverageMeter.c | 12 +-- Machine.c | 60 +++++++++++++ Machine.h | 89 ++++++++++++++++++++ MainPanel.c | 23 +++-- Makefile.am | 2 + MemorySwapMeter.c | 4 +- Meter.c | 12 +-- Meter.h | 6 +- NetworkIOMeter.c | 6 +- Process.c | 52 +++++++----- Process.h | 11 ++- ProcessList.c | 74 ++++------------- ProcessList.h | 49 +---------- ScreenManager.c | 33 ++++---- ScreenManager.h | 6 +- TasksMeter.c | 9 +- darwin/DarwinProcess.c | 9 +- darwin/DarwinProcess.h | 4 +- darwin/DarwinProcessList.c | 31 ++++--- darwin/DarwinProcessList.h | 8 +- darwin/Platform.c | 15 ++-- dragonflybsd/DragonFlyBSDProcess.c | 4 +- dragonflybsd/DragonFlyBSDProcess.h | 4 +- dragonflybsd/DragonFlyBSDProcessList.c | 33 +++++--- dragonflybsd/DragonFlyBSDProcessList.h | 8 +- dragonflybsd/Platform.c | 11 +-- freebsd/FreeBSDProcess.c | 4 +- freebsd/FreeBSDProcess.h | 4 +- freebsd/FreeBSDProcessList.c | 75 ++++++++++------- freebsd/FreeBSDProcessList.h | 8 +- freebsd/Platform.c | 31 +++---- linux/HugePageMeter.c | 2 +- linux/LinuxProcess.c | 6 +- linux/LinuxProcess.h | 4 +- linux/LinuxProcessList.c | 106 +++++++++++++---------- linux/LinuxProcessList.h | 8 +- linux/Platform.c | 39 ++++----- netbsd/NetBSDProcess.c | 4 +- netbsd/NetBSDProcess.h | 4 +- netbsd/NetBSDProcessList.c | 64 ++++++++------ netbsd/NetBSDProcessList.h | 8 +- netbsd/Platform.c | 21 ++--- openbsd/OpenBSDProcess.c | 4 +- openbsd/OpenBSDProcess.h | 4 +- openbsd/OpenBSDProcessList.c | 60 ++++++++----- openbsd/OpenBSDProcessList.h | 8 +- openbsd/Platform.c | 21 ++--- pcp/PCPDynamicColumn.c | 2 +- pcp/PCPProcess.c | 6 +- pcp/PCPProcess.h | 4 +- pcp/PCPProcessList.c | 86 +++++++++++-------- pcp/PCPProcessList.h | 8 +- pcp/Platform.c | 34 ++++---- solaris/Platform.c | 27 +++--- solaris/SolarisProcess.c | 4 +- solaris/SolarisProcess.h | 4 +- solaris/SolarisProcessList.c | 64 ++++++++------ solaris/SolarisProcessList.h | 8 +- unsupported/UnsupportedProcess.c | 6 +- unsupported/UnsupportedProcess.h | 4 +- unsupported/UnsupportedProcessList.c | 24 ++++-- unsupported/UnsupportedProcessList.h | 8 +- 81 files changed, 989 insertions(+), 718 deletions(-) create mode 100644 Machine.c create mode 100644 Machine.h diff --git a/Action.c b/Action.c index 73b86062c..d6b2a1cea 100644 --- a/Action.c +++ b/Action.c @@ -27,6 +27,7 @@ in the source distribution for its full text. #include "MainPanel.h" #include "OpenFilesScreen.h" #include "Process.h" +#include "ProcessList.h" #include "ProcessLocksScreen.h" #include "ProvideCurses.h" #include "Scheduling.h" @@ -45,9 +46,10 @@ in the source distribution for its full text. Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess) { MainPanel* mainPanel = st->mainPanel; Header* header = st->header; + Machine* host = st->host; int y = ((Panel*)mainPanel)->y; - ScreenManager* scr = ScreenManager_new(header, st->settings, st, false); + ScreenManager* scr = ScreenManager_new(header, host, st, false); scr->allowFocusChange = false; ScreenManager_add(scr, list, x); ScreenManager_add(scr, (Panel*)mainPanel, -1); @@ -55,13 +57,13 @@ Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess) int ch; bool unfollow = false; int pid = followProcess ? MainPanel_selectedPid(mainPanel) : -1; - if (followProcess && header->pl->following == -1) { - header->pl->following = pid; + if (followProcess && host->pl->following == -1) { + host->pl->following = pid; unfollow = true; } ScreenManager_run(scr, &panelFocus, &ch, NULL); if (unfollow) { - header->pl->following = -1; + host->pl->following = -1; } ScreenManager_delete(scr); Panel_move((Panel*)mainPanel, 0, y); @@ -84,12 +86,13 @@ Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess) // ---------------------------------------- static void Action_runSetup(State* st) { - ScreenManager* scr = ScreenManager_new(st->header, st->settings, st, true); - CategoriesPanel_new(scr, st->settings, st->header, st->pl); + const Settings* settings = st->host->settings; + ScreenManager* scr = ScreenManager_new(st->header, st->host, st, true); + CategoriesPanel_new(scr, st->header, st->host); ScreenManager_run(scr, NULL, NULL, "Setup"); ScreenManager_delete(scr); - if (st->settings->changed) { - CRT_setMouse(st->settings->enableMouse); + if (settings->changed) { + CRT_setMouse(settings->enableMouse); Header_writeBackToSettings(st->header); } } @@ -166,7 +169,8 @@ static Htop_Reaction actionSetSortColumn(State* st) { Htop_Reaction reaction = HTOP_OK; Panel* sortPanel = Panel_new(0, 0, 0, 0, Class(ListItem), true, FunctionBar_newEnterEsc("Sort ", "Cancel ")); Panel_setHeader(sortPanel, "Sort by"); - const Settings* settings = st->settings; + Machine* host = st->host; + Settings* settings = host->settings; const ProcessField* fields = settings->ss->fields; Hashtable* dynamicColumns = settings->dynamicColumns; for (int i = 0; fields[i]; i++) { @@ -187,74 +191,80 @@ static Htop_Reaction actionSetSortColumn(State* st) { } const ListItem* field = (const ListItem*) Action_pickFromVector(st, sortPanel, 14, false); if (field) { - reaction |= Action_setSortKey(st->settings, field->key); + reaction |= Action_setSortKey(settings, field->key); } Object_delete(sortPanel); - st->pl->needsSort = true; + host->pl->needsSort = true; return reaction | HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR; } static Htop_Reaction actionSortByPID(State* st) { - return Action_setSortKey(st->settings, PID); + return Action_setSortKey(st->host->settings, PID); } static Htop_Reaction actionSortByMemory(State* st) { - return Action_setSortKey(st->settings, PERCENT_MEM); + return Action_setSortKey(st->host->settings, PERCENT_MEM); } static Htop_Reaction actionSortByCPU(State* st) { - return Action_setSortKey(st->settings, PERCENT_CPU); + return Action_setSortKey(st->host->settings, PERCENT_CPU); } static Htop_Reaction actionSortByTime(State* st) { - return Action_setSortKey(st->settings, TIME); + return Action_setSortKey(st->host->settings, TIME); } static Htop_Reaction actionToggleKernelThreads(State* st) { - st->settings->hideKernelThreads = !st->settings->hideKernelThreads; - st->settings->lastUpdate++; + Settings* settings = st->host->settings; + settings->hideKernelThreads = !settings->hideKernelThreads; + settings->lastUpdate++; return HTOP_RECALCULATE | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING; } static Htop_Reaction actionToggleUserlandThreads(State* st) { - st->settings->hideUserlandThreads = !st->settings->hideUserlandThreads; - st->settings->lastUpdate++; + Settings* settings = st->host->settings; + settings->hideUserlandThreads = !settings->hideUserlandThreads; + settings->lastUpdate++; return HTOP_RECALCULATE | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING; } static Htop_Reaction actionToggleRunningInContainer(State* st) { - st->settings->hideRunningInContainer = !st->settings->hideRunningInContainer; - st->settings->lastUpdate++; + Settings* settings = st->host->settings; + settings->hideRunningInContainer = !settings->hideRunningInContainer; + settings->lastUpdate++; return HTOP_RECALCULATE | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING; } static Htop_Reaction actionToggleProgramPath(State* st) { - st->settings->showProgramPath = !st->settings->showProgramPath; - st->settings->lastUpdate++; + Settings* settings = st->host->settings; + settings->showProgramPath = !settings->showProgramPath; + settings->lastUpdate++; return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING; } static Htop_Reaction actionToggleMergedCommand(State* st) { - st->settings->showMergedCommand = !st->settings->showMergedCommand; - st->settings->lastUpdate++; + Settings* settings = st->host->settings; + settings->showMergedCommand = !settings->showMergedCommand; + settings->lastUpdate++; return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING | HTOP_UPDATE_PANELHDR; } static Htop_Reaction actionToggleTreeView(State* st) { - ScreenSettings* ss = st->settings->ss; + Machine* host = st->host; + ScreenSettings* ss = host->settings->ss; ss->treeView = !ss->treeView; if (!ss->allBranchesCollapsed) - ProcessList_expandTree(st->pl); + ProcessList_expandTree(host->pl); - st->pl->needsSort = true; + host->pl->needsSort = true; return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR; } @@ -265,22 +275,24 @@ static Htop_Reaction actionToggleHideMeters(State* st) { } static Htop_Reaction actionExpandOrCollapseAllBranches(State* st) { - ScreenSettings* ss = st->settings->ss; + Machine* host = st->host; + ScreenSettings* ss = host->settings->ss; if (!ss->treeView) { return HTOP_OK; } ss->allBranchesCollapsed = !ss->allBranchesCollapsed; if (ss->allBranchesCollapsed) - ProcessList_collapseAllBranches(st->pl); + ProcessList_collapseAllBranches(host->pl); else - ProcessList_expandTree(st->pl); + ProcessList_expandTree(host->pl); return HTOP_REFRESH | HTOP_SAVE_SETTINGS; } static Htop_Reaction actionIncFilter(State* st) { + Machine* host = st->host; IncSet* inc = (st->mainPanel)->inc; IncSet_activate(inc, INC_FILTER, (Panel*)st->mainPanel); - st->pl->incFilter = IncSet_filter(inc); + host->pl->incFilter = IncSet_filter(inc); return HTOP_REFRESH | HTOP_KEEP_FOLLOWING; } @@ -307,13 +319,14 @@ static Htop_Reaction actionLowerPriority(State* st) { } static Htop_Reaction actionInvertSortOrder(State* st) { - ScreenSettings_invertSortOrder(st->settings->ss); - st->pl->needsSort = true; + Machine* host = st->host; + ScreenSettings_invertSortOrder(host->settings->ss); + host->pl->needsSort = true; return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING | HTOP_UPDATE_PANELHDR; } static Htop_Reaction actionExpandOrCollapse(State* st) { - if (!st->settings->ss->treeView) + if (!st->host->settings->ss->treeView) return HTOP_OK; bool changed = expandCollapse((Panel*)st->mainPanel); @@ -321,7 +334,7 @@ static Htop_Reaction actionExpandOrCollapse(State* st) { } static Htop_Reaction actionCollapseIntoParent(State* st) { - if (!st->settings->ss->treeView) { + if (!st->host->settings->ss->treeView) { return HTOP_OK; } bool changed = collapseIntoParent((Panel*)st->mainPanel); @@ -329,11 +342,11 @@ static Htop_Reaction actionCollapseIntoParent(State* st) { } static Htop_Reaction actionExpandCollapseOrSortColumn(State* st) { - return st->settings->ss->treeView ? actionExpandOrCollapse(st) : actionSetSortColumn(st); + return st->host->settings->ss->treeView ? actionExpandOrCollapse(st) : actionSetSortColumn(st); } static Htop_Reaction actionNextScreen(State* st) { - Settings* settings = st->settings; + Settings* settings = st->host->settings; settings->ssIndex++; if (settings->ssIndex == settings->nScreens) { settings->ssIndex = 0; @@ -343,7 +356,7 @@ static Htop_Reaction actionNextScreen(State* st) { } static Htop_Reaction actionPrevScreen(State* st) { - Settings* settings = st->settings; + Settings* settings = st->host->settings; if (settings->ssIndex == 0) { settings->ssIndex = settings->nScreens - 1; } else { @@ -379,7 +392,8 @@ static Htop_Reaction actionSetAffinity(State* st) { if (Settings_isReadonly()) return HTOP_OK; - if (st->pl->activeCPUs == 1) + Machine* host = st->host; + if (host->activeCPUs == 1) return HTOP_OK; #if (defined(HAVE_LIBHWLOC) || defined(HAVE_AFFINITY)) @@ -387,17 +401,17 @@ static Htop_Reaction actionSetAffinity(State* st) { if (!p) return HTOP_OK; - Affinity* affinity1 = Affinity_get(p, st->pl); + Affinity* affinity1 = Affinity_get(p, host); if (!affinity1) return HTOP_OK; int width; - Panel* affinityPanel = AffinityPanel_new(st->pl, affinity1, &width); + Panel* affinityPanel = AffinityPanel_new(host, affinity1, &width); Affinity_delete(affinity1); const void* set = Action_pickFromVector(st, affinityPanel, width, true); if (set) { - Affinity* affinity2 = AffinityPanel_getAffinity(affinityPanel, st->pl); + Affinity* affinity2 = AffinityPanel_getAffinity(affinityPanel, host); bool ok = MainPanel_foreachProcess(st->mainPanel, Affinity_set, (Arg) { .v = affinity2 }, NULL); if (!ok) beep(); @@ -479,16 +493,17 @@ static Htop_Reaction actionKill(State* st) { static Htop_Reaction actionFilterByUser(State* st) { Panel* usersPanel = Panel_new(0, 0, 0, 0, Class(ListItem), true, FunctionBar_newEnterEsc("Show ", "Cancel ")); Panel_setHeader(usersPanel, "Show processes of:"); - UsersTable_foreach(st->ut, addUserToVector, usersPanel); + Machine* host = st->host; + UsersTable_foreach(host->usersTable, addUserToVector, usersPanel); Vector_insertionSort(usersPanel->items); ListItem* allUsers = ListItem_new("All users", -1); Panel_insert(usersPanel, 0, (Object*) allUsers); const ListItem* picked = (ListItem*) Action_pickFromVector(st, usersPanel, 19, false); if (picked) { if (picked == allUsers) { - st->pl->userId = (uid_t)-1; + host->userId = (uid_t)-1; } else { - Action_setUserOnly(ListItem_getRef(picked), &(st->pl->userId)); + Action_setUserOnly(ListItem_getRef(picked), &host->userId); } } Panel_delete((Object*)usersPanel); @@ -496,7 +511,7 @@ static Htop_Reaction actionFilterByUser(State* st) { } Htop_Reaction Action_follow(State* st) { - st->pl->following = MainPanel_selectedPid(st->mainPanel); + st->host->pl->following = MainPanel_selectedPid(st->mainPanel); Panel_setSelectionColor((Panel*)st->mainPanel, PANEL_SELECTION_FOLLOW); return HTOP_KEEP_FOLLOWING; } @@ -660,7 +675,7 @@ static Htop_Reaction actionHelp(State* st) { addbartext(CRT_colors[CPU_NICE_TEXT], "", "low"); addbartext(CRT_colors[CPU_NORMAL], "/", "normal"); addbartext(CRT_colors[CPU_SYSTEM], "/", "kernel"); - if (st->settings->detailedCPUTime) { + if (st->host->settings->detailedCPUTime) { addbartext(CRT_colors[CPU_IRQ], "/", "irq"); addbartext(CRT_colors[CPU_SOFTIRQ], "/", "soft-irq"); addbartext(CRT_colors[CPU_STEAL], "/", "steal"); diff --git a/Action.h b/Action.h index 04d090f8f..3540e93e3 100644 --- a/Action.h +++ b/Action.h @@ -13,10 +13,10 @@ in the source distribution for its full text. #include #include "Header.h" +#include "Machine.h" #include "Object.h" #include "Panel.h" #include "Process.h" -#include "ProcessList.h" #include "Settings.h" #include "UsersTable.h" @@ -36,9 +36,7 @@ typedef enum { struct MainPanel_; // IWYU pragma: keep typedef struct State_ { - Settings* settings; - UsersTable* ut; - ProcessList* pl; + Machine* host; struct MainPanel_* mainPanel; Header* header; bool pauseUpdate; @@ -47,12 +45,13 @@ typedef struct State_ { } State; static inline bool State_hideFunctionBar(const State* st) { - return st->settings->hideFunctionBar == 2 || (st->settings->hideFunctionBar == 1 && st->hideSelection); + const Settings* settings = st->host->settings; + return settings->hideFunctionBar == 2 || (settings->hideFunctionBar == 1 && st->hideSelection); } typedef Htop_Reaction (*Htop_Action)(State* st); -Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess); +Object* Action_pickFromVector(State* st, Panel* list, int x, bool follow); bool Action_setUserOnly(const char* userName, uid_t* userId); diff --git a/Affinity.c b/Affinity.c index dc6a7fba0..f7c597bfc 100644 --- a/Affinity.c +++ b/Affinity.c @@ -27,11 +27,11 @@ in the source distribution for its full text. #endif -Affinity* Affinity_new(ProcessList* pl) { +Affinity* Affinity_new(Machine* host) { Affinity* this = xCalloc(1, sizeof(Affinity)); this->size = 8; this->cpus = xCalloc(this->size, sizeof(unsigned int)); - this->pl = pl; + this->host = host; return this; } @@ -52,14 +52,14 @@ void Affinity_add(Affinity* this, unsigned int id) { #if defined(HAVE_LIBHWLOC) -Affinity* Affinity_get(const Process* proc, ProcessList* pl) { +Affinity* Affinity_get(const Process* proc, Machine* host) { hwloc_cpuset_t cpuset = hwloc_bitmap_alloc(); - bool ok = (hwloc_get_proc_cpubind(pl->topology, proc->pid, cpuset, HTOP_HWLOC_CPUBIND_FLAG) == 0); + bool ok = (hwloc_get_proc_cpubind(host->topology, proc->pid, cpuset, HTOP_HWLOC_CPUBIND_FLAG) == 0); Affinity* affinity = NULL; if (ok) { - affinity = Affinity_new(pl); + affinity = Affinity_new(host); if (hwloc_bitmap_last(cpuset) == -1) { - for (unsigned int i = 0; i < pl->existingCPUs; i++) { + for (unsigned int i = 0; i < host->existingCPUs; i++) { Affinity_add(affinity, i); } } else { @@ -79,21 +79,21 @@ bool Affinity_set(Process* proc, Arg arg) { for (unsigned int i = 0; i < this->used; i++) { hwloc_bitmap_set(cpuset, this->cpus[i]); } - bool ok = (hwloc_set_proc_cpubind(this->pl->topology, proc->pid, cpuset, HTOP_HWLOC_CPUBIND_FLAG) == 0); + bool ok = (hwloc_set_proc_cpubind(this->host->topology, proc->pid, cpuset, HTOP_HWLOC_CPUBIND_FLAG) == 0); hwloc_bitmap_free(cpuset); return ok; } #elif defined(HAVE_AFFINITY) -Affinity* Affinity_get(const Process* proc, ProcessList* pl) { +Affinity* Affinity_get(const Process* proc, Machine* host) { cpu_set_t cpuset; bool ok = (sched_getaffinity(proc->pid, sizeof(cpu_set_t), &cpuset) == 0); if (!ok) return NULL; - Affinity* affinity = Affinity_new(pl); - for (unsigned int i = 0; i < pl->existingCPUs; i++) { + Affinity* affinity = Affinity_new(host); + for (unsigned int i = 0; i < host->existingCPUs; i++) { if (CPU_ISSET(i, &cpuset)) { Affinity_add(affinity, i); } diff --git a/Affinity.h b/Affinity.h index 5e7bfe2e6..58d9bd733 100644 --- a/Affinity.h +++ b/Affinity.h @@ -3,14 +3,14 @@ /* htop - Affinity.h (C) 2004-2011 Hisham H. Muhammad -(C) 2020 Red Hat, Inc. All Rights Reserved. +(C) 2020,2023 Red Hat, Inc. All Rights Reserved. Released under the GNU GPLv2+, see the COPYING file in the source distribution for its full text. */ #include "config.h" // IWYU pragma: keep -#include "ProcessList.h" +#include "Machine.h" #if defined(HAVE_LIBHWLOC) || defined(HAVE_AFFINITY) #include @@ -26,13 +26,13 @@ in the source distribution for its full text. typedef struct Affinity_ { - ProcessList* pl; + Machine* host; unsigned int size; unsigned int used; unsigned int* cpus; } Affinity; -Affinity* Affinity_new(ProcessList* pl); +Affinity* Affinity_new(Machine* host); void Affinity_delete(Affinity* this); @@ -40,7 +40,7 @@ void Affinity_add(Affinity* this, unsigned int id); #if defined(HAVE_LIBHWLOC) || defined(HAVE_AFFINITY) -Affinity* Affinity_get(const Process* proc, ProcessList* pl); +Affinity* Affinity_get(const Process* proc, Machine* host); bool Affinity_set(Process* proc, Arg arg); diff --git a/AffinityPanel.c b/AffinityPanel.c index b7243971a..1214a84f4 100644 --- a/AffinityPanel.c +++ b/AffinityPanel.c @@ -122,7 +122,7 @@ static MaskItem* MaskItem_newSingleton(const char* text, int cpu, bool isSet) { typedef struct AffinityPanel_ { Panel super; - ProcessList* pl; + Machine* host; bool topoView; Vector* cpuids; unsigned width; @@ -272,7 +272,7 @@ static MaskItem* AffinityPanel_addObject(AffinityPanel* this, hwloc_obj_t obj, u char buf[64], indent_buf[left + 1]; if (obj->type == HWLOC_OBJ_PU) { - index = Settings_cpuId(this->pl->settings, obj->os_index); + index = Settings_cpuId(this->host->settings, obj->os_index); type_name = "CPU"; index_prefix = ""; } @@ -357,12 +357,12 @@ static const char* const AffinityPanelFunctions[] = { static const char* const AffinityPanelKeys[] = {"Enter", "Esc", "F1", "F2", "F3"}; static const int AffinityPanelEvents[] = {13, 27, KEY_F(1), KEY_F(2), KEY_F(3)}; -Panel* AffinityPanel_new(ProcessList* pl, const Affinity* affinity, int* width) { +Panel* AffinityPanel_new(Machine* host, const Affinity* affinity, int* width) { AffinityPanel* this = AllocThis(AffinityPanel); Panel* super = (Panel*) this; Panel_init(super, 1, 1, 1, 1, Class(MaskItem), false, FunctionBar_new(AffinityPanelFunctions, AffinityPanelKeys, AffinityPanelEvents)); - this->pl = pl; + this->host = host; /* defaults to 15, this also includes the gap between the panels, * but this will be added by the caller */ this->width = 14; @@ -370,25 +370,25 @@ Panel* AffinityPanel_new(ProcessList* pl, const Affinity* affinity, int* width) this->cpuids = Vector_new(Class(MaskItem), true, DEFAULT_SIZE); #ifdef HAVE_LIBHWLOC - this->topoView = pl->settings->topologyAffinity; + this->topoView = host->settings->topologyAffinity; #else this->topoView = false; #endif #ifdef HAVE_LIBHWLOC - this->allCpuset = hwloc_topology_get_complete_cpuset(pl->topology); + this->allCpuset = hwloc_topology_get_complete_cpuset(host->topology); this->workCpuset = hwloc_bitmap_alloc(); #endif Panel_setHeader(super, "Use CPUs:"); unsigned int curCpu = 0; - for (unsigned int i = 0; i < pl->existingCPUs; i++) { - if (!ProcessList_isCPUonline(this->pl, i)) + for (unsigned int i = 0; i < host->existingCPUs; i++) { + if (!Machine_isCPUonline(host, i)) continue; char number[16]; - xSnprintf(number, 9, "CPU %d", Settings_cpuId(pl->settings, i)); + xSnprintf(number, 9, "CPU %d", Settings_cpuId(host->settings, i)); unsigned cpu_width = 4 + strlen(number); if (cpu_width > this->width) { this->width = cpu_width; @@ -408,7 +408,7 @@ Panel* AffinityPanel_new(ProcessList* pl, const Affinity* affinity, int* width) } #ifdef HAVE_LIBHWLOC - this->topoRoot = AffinityPanel_buildTopology(this, hwloc_get_root_obj(pl->topology), 0, NULL); + this->topoRoot = AffinityPanel_buildTopology(this, hwloc_get_root_obj(host->topology), 0, NULL); #endif if (width) { @@ -420,9 +420,9 @@ Panel* AffinityPanel_new(ProcessList* pl, const Affinity* affinity, int* width) return super; } -Affinity* AffinityPanel_getAffinity(Panel* super, ProcessList* pl) { +Affinity* AffinityPanel_getAffinity(Panel* super, Machine* host) { const AffinityPanel* this = (AffinityPanel*) super; - Affinity* affinity = Affinity_new(pl); + Affinity* affinity = Affinity_new(host); #ifdef HAVE_LIBHWLOC int i; diff --git a/AffinityPanel.h b/AffinityPanel.h index 87b1b850f..f5b97b933 100644 --- a/AffinityPanel.h +++ b/AffinityPanel.h @@ -8,14 +8,14 @@ in the source distribution for its full text. */ #include "Affinity.h" +#include "Machine.h" #include "Panel.h" -#include "ProcessList.h" extern const PanelClass AffinityPanel_class; -Panel* AffinityPanel_new(ProcessList* pl, const Affinity* affinity, int* width); +Panel* AffinityPanel_new(Machine* host, const Affinity* affinity, int* width); -Affinity* AffinityPanel_getAffinity(Panel* super, ProcessList* pl); +Affinity* AffinityPanel_getAffinity(Panel* super, Machine* host); #endif diff --git a/AvailableMetersPanel.c b/AvailableMetersPanel.c index aa6d7795a..de1dd2114 100644 --- a/AvailableMetersPanel.c +++ b/AvailableMetersPanel.c @@ -78,8 +78,9 @@ static HandlerResult AvailableMetersPanel_eventHandler(Panel* super, int ch) { } } if (update) { - this->settings->changed = true; - this->settings->lastUpdate++; + Settings* settings = this->host->settings; + settings->changed = true; + settings->lastUpdate++; Header_calculateHeight(header); Header_updateData(header); Header_draw(header); @@ -97,12 +98,12 @@ const PanelClass AvailableMetersPanel_class = { }; // Handle (&CPUMeter_class) entries in the AvailableMetersPanel -static void AvailableMetersPanel_addCPUMeters(Panel* super, const MeterClass* type, const ProcessList* pl) { - if (pl->existingCPUs > 1) { +static void AvailableMetersPanel_addCPUMeters(Panel* super, const MeterClass* type, const Machine* host) { + if (host->existingCPUs > 1) { Panel_add(super, (Object*) ListItem_new("CPU average", 0)); - for (unsigned int i = 1; i <= pl->existingCPUs; i++) { + for (unsigned int i = 1; i <= host->existingCPUs; i++) { char buffer[50]; - xSnprintf(buffer, sizeof(buffer), "%s %d", type->uiName, Settings_cpuId(pl->settings, i - 1)); + xSnprintf(buffer, sizeof(buffer), "%s %d", type->uiName, Settings_cpuId(host->settings, i - 1)); Panel_add(super, (Object*) ListItem_new(buffer, i)); } } else { @@ -141,13 +142,13 @@ static void AvailableMetersPanel_addPlatformMeter(Panel* super, const MeterClass Panel_add(super, (Object*) ListItem_new(label, offset << 16)); } -AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* header, size_t columns, MetersPanel** meterPanels, ScreenManager* scr, const ProcessList* pl) { +AvailableMetersPanel* AvailableMetersPanel_new(Machine* host, Header* header, size_t columns, MetersPanel** meterPanels, ScreenManager* scr) { AvailableMetersPanel* this = AllocThis(AvailableMetersPanel); Panel* super = (Panel*) this; FunctionBar* fuBar = FunctionBar_newEnterEsc("Add ", "Done "); Panel_init(super, 1, 1, 1, 1, Class(ListItem), true, fuBar); - this->settings = settings; + this->host = host; this->header = header; this->columns = columns; this->meterPanels = meterPanels; @@ -162,11 +163,11 @@ AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* heade const MeterClass* type = Platform_meterTypes[i]; assert(type != &CPUMeter_class); if (type == &DynamicMeter_class) - AvailableMetersPanel_addDynamicMeters(super, settings, i); + AvailableMetersPanel_addDynamicMeters(super, host->settings, i); else AvailableMetersPanel_addPlatformMeter(super, type, i); } - AvailableMetersPanel_addCPUMeters(super, &CPUMeter_class, pl); + AvailableMetersPanel_addCPUMeters(super, &CPUMeter_class, host); return this; } diff --git a/AvailableMetersPanel.h b/AvailableMetersPanel.h index 1c0555af2..fad1e6e8a 100644 --- a/AvailableMetersPanel.h +++ b/AvailableMetersPanel.h @@ -10,18 +10,16 @@ in the source distribution for its full text. #include #include "Header.h" +#include "Machine.h" #include "MetersPanel.h" #include "Panel.h" -#include "ProcessList.h" #include "ScreenManager.h" -#include "Settings.h" typedef struct AvailableMetersPanel_ { Panel super; ScreenManager* scr; - - Settings* settings; + Machine* host; Header* header; size_t columns; MetersPanel** meterPanels; @@ -29,6 +27,6 @@ typedef struct AvailableMetersPanel_ { extern const PanelClass AvailableMetersPanel_class; -AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* header, size_t columns, MetersPanel** meterPanels, ScreenManager* scr, const ProcessList* pl); +AvailableMetersPanel* AvailableMetersPanel_new(Machine* host, Header* header, size_t columns, MetersPanel** meterPanels, ScreenManager* scr); #endif diff --git a/CPUMeter.c b/CPUMeter.c index ba0059561..a946aa7d7 100644 --- a/CPUMeter.c +++ b/CPUMeter.c @@ -40,11 +40,12 @@ typedef struct CPUMeterData_ { static void CPUMeter_init(Meter* this) { unsigned int cpu = this->param; + const Machine* host = this->host; if (cpu == 0) { Meter_setCaption(this, "Avg"); - } else if (this->pl->activeCPUs > 1) { + } else if (host->activeCPUs > 1) { char caption[10]; - xSnprintf(caption, sizeof(caption), "%3u", Settings_cpuId(this->pl->settings, cpu - 1)); + xSnprintf(caption, sizeof(caption), "%3u", Settings_cpuId(host->settings, cpu - 1)); Meter_setCaption(this, caption); } } @@ -60,8 +61,11 @@ static void CPUMeter_getUiName(const Meter* this, char* buffer, size_t length) { static void CPUMeter_updateValues(Meter* this) { memset(this->values, 0, sizeof(double) * CPU_METER_ITEMCOUNT); + const Machine* host = this->host; + const Settings* settings = host->settings; + unsigned int cpu = this->param; - if (cpu > this->pl->existingCPUs) { + if (cpu > host->existingCPUs) { xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "absent"); return; } @@ -76,11 +80,11 @@ static void CPUMeter_updateValues(Meter* this) { char cpuFrequencyBuffer[16] = { 0 }; char cpuTemperatureBuffer[16] = { 0 }; - if (this->pl->settings->showCPUUsage) { + if (settings->showCPUUsage) { xSnprintf(cpuUsageBuffer, sizeof(cpuUsageBuffer), "%.1f%%", percent); } - if (this->pl->settings->showCPUFrequency) { + if (settings->showCPUFrequency) { double cpuFrequency = this->values[CPU_METER_FREQUENCY]; if (isnan(cpuFrequency)) { xSnprintf(cpuFrequencyBuffer, sizeof(cpuFrequencyBuffer), "N/A"); @@ -90,11 +94,11 @@ static void CPUMeter_updateValues(Meter* this) { } #ifdef BUILD_WITH_CPU_TEMP - if (this->pl->settings->showCPUTemperature) { + if (settings->showCPUTemperature) { double cpuTemperature = this->values[CPU_METER_TEMPERATURE]; if (isnan(cpuTemperature)) xSnprintf(cpuTemperatureBuffer, sizeof(cpuTemperatureBuffer), "N/A"); - else if (this->pl->settings->degreeFahrenheit) + else if (settings->degreeFahrenheit) xSnprintf(cpuTemperatureBuffer, sizeof(cpuTemperatureBuffer), "%3d%sF", (int)(cpuTemperature * 9 / 5 + 32), CRT_degreeSign); else xSnprintf(cpuTemperatureBuffer, sizeof(cpuTemperatureBuffer), "%d%sC", (int)cpuTemperature, CRT_degreeSign); @@ -113,8 +117,10 @@ static void CPUMeter_display(const Object* cast, RichString* out) { char buffer[50]; int len; const Meter* this = (const Meter*)cast; + const Machine* host = this->host; + const Settings* settings = host->settings; - if (this->param > this->pl->existingCPUs) { + if (this->param > host->existingCPUs) { RichString_appendAscii(out, CRT_colors[METER_SHADOW], " absent"); return; } @@ -127,7 +133,7 @@ static void CPUMeter_display(const Object* cast, RichString* out) { len = xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_NORMAL]); RichString_appendAscii(out, CRT_colors[METER_TEXT], ":"); RichString_appendnAscii(out, CRT_colors[CPU_NORMAL], buffer, len); - if (this->pl->settings->detailedCPUTime) { + if (settings->detailedCPUTime) { len = xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_KERNEL]); RichString_appendAscii(out, CRT_colors[METER_TEXT], "sy:"); RichString_appendnAscii(out, CRT_colors[CPU_SYSTEM], buffer, len); @@ -167,7 +173,7 @@ static void CPUMeter_display(const Object* cast, RichString* out) { } } - if (this->pl->settings->showCPUFrequency) { + if (settings->showCPUFrequency) { char cpuFrequencyBuffer[10]; double cpuFrequency = this->values[CPU_METER_FREQUENCY]; if (isnan(cpuFrequency)) { @@ -180,12 +186,12 @@ static void CPUMeter_display(const Object* cast, RichString* out) { } #ifdef BUILD_WITH_CPU_TEMP - if (this->pl->settings->showCPUTemperature) { + if (settings->showCPUTemperature) { char cpuTemperatureBuffer[10]; double cpuTemperature = this->values[CPU_METER_TEMPERATURE]; if (isnan(cpuTemperature)) { len = xSnprintf(cpuTemperatureBuffer, sizeof(cpuTemperatureBuffer), "N/A"); - } else if (this->pl->settings->degreeFahrenheit) { + } else if (settings->degreeFahrenheit) { len = xSnprintf(cpuTemperatureBuffer, sizeof(cpuTemperatureBuffer), "%5.1f%sF", cpuTemperature * 9 / 5 + 32, CRT_degreeSign); } else { len = xSnprintf(cpuTemperatureBuffer, sizeof(cpuTemperatureBuffer), "%5.1f%sC", cpuTemperature, CRT_degreeSign); @@ -226,7 +232,7 @@ static void AllCPUsMeter_updateValues(Meter* this) { } static void CPUMeterCommonInit(Meter* this, int ncol) { - unsigned int cpus = this->pl->existingCPUs; + unsigned int cpus = this->host->existingCPUs; CPUMeterData* data = this->meterData; if (!data) { data = this->meterData = xMalloc(sizeof(CPUMeterData)); @@ -238,7 +244,7 @@ static void CPUMeterCommonInit(Meter* this, int ncol) { AllCPUsMeter_getRange(this, &start, &count); for (int i = 0; i < count; i++) { if (!meters[i]) - meters[i] = Meter_new(this->pl, start + i + 1, (const MeterClass*) Class(CPUMeter)); + meters[i] = Meter_new(this->host, start + i + 1, (const MeterClass*) Class(CPUMeter)); Meter_init(meters[i]); } diff --git a/CategoriesPanel.c b/CategoriesPanel.c index 6e905ce9e..ba7ee5030 100644 --- a/CategoriesPanel.c +++ b/CategoriesPanel.c @@ -41,11 +41,12 @@ static void CategoriesPanel_delete(Object* object) { static void CategoriesPanel_makeMetersPage(CategoriesPanel* this) { size_t columns = HeaderLayout_getColumns(this->scr->header->headerLayout); MetersPanel** meterPanels = xMallocArray(columns, sizeof(MetersPanel*)); + Settings* settings = this->host->settings; for (size_t i = 0; i < columns; i++) { char titleBuffer[32]; xSnprintf(titleBuffer, sizeof(titleBuffer), "Column %zu", i + 1); - meterPanels[i] = MetersPanel_new(this->settings, titleBuffer, this->header->columns[i], this->scr); + meterPanels[i] = MetersPanel_new(settings, titleBuffer, this->header->columns[i], this->scr); if (i != 0) { meterPanels[i]->leftNeighbor = meterPanels[i - 1]; @@ -55,31 +56,35 @@ static void CategoriesPanel_makeMetersPage(CategoriesPanel* this) { ScreenManager_add(this->scr, (Panel*) meterPanels[i], 20); } - Panel* availableMeters = (Panel*) AvailableMetersPanel_new(this->settings, this->header, columns, meterPanels, this->scr, this->pl); + Panel* availableMeters = (Panel*) AvailableMetersPanel_new(this->host, this->header, columns, meterPanels, this->scr); ScreenManager_add(this->scr, availableMeters, -1); } static void CategoriesPanel_makeDisplayOptionsPage(CategoriesPanel* this) { - Panel* displayOptions = (Panel*) DisplayOptionsPanel_new(this->settings, this->scr); + Settings* settings = this->host->settings; + Panel* displayOptions = (Panel*) DisplayOptionsPanel_new(settings, this->scr); ScreenManager_add(this->scr, displayOptions, -1); } static void CategoriesPanel_makeColorsPage(CategoriesPanel* this) { - Panel* colors = (Panel*) ColorsPanel_new(this->settings); + Settings* settings = this->host->settings; + Panel* colors = (Panel*) ColorsPanel_new(settings); ScreenManager_add(this->scr, colors, -1); } static void CategoriesPanel_makeScreensPage(CategoriesPanel* this) { - Panel* screens = (Panel*) ScreensPanel_new(this->settings); + Settings* settings = this->host->settings; + Panel* screens = (Panel*) ScreensPanel_new(settings); Panel* columns = (Panel*) ((ScreensPanel*)screens)->columns; - Panel* availableColumns = (Panel*) AvailableColumnsPanel_new(columns, this->settings->dynamicColumns); + Panel* availableColumns = (Panel*) AvailableColumnsPanel_new(columns, settings->dynamicColumns); ScreenManager_add(this->scr, screens, 20); ScreenManager_add(this->scr, columns, 20); ScreenManager_add(this->scr, availableColumns, -1); } static void CategoriesPanel_makeHeaderOptionsPage(CategoriesPanel* this) { - Panel* colors = (Panel*) HeaderOptionsPanel_new(this->settings, this->scr); + Settings* settings = this->host->settings; + Panel* colors = (Panel*) HeaderOptionsPanel_new(settings, this->scr); ScreenManager_add(this->scr, colors, -1); } @@ -149,16 +154,15 @@ const PanelClass CategoriesPanel_class = { .eventHandler = CategoriesPanel_eventHandler }; -CategoriesPanel* CategoriesPanel_new(ScreenManager* scr, Settings* settings, Header* header, ProcessList* pl) { +CategoriesPanel* CategoriesPanel_new(ScreenManager* scr, Header* header, Machine* host) { CategoriesPanel* this = AllocThis(CategoriesPanel); Panel* super = (Panel*) this; FunctionBar* fuBar = FunctionBar_new(CategoriesFunctions, NULL, NULL); Panel_init(super, 1, 1, 1, 1, Class(ListItem), true, fuBar); this->scr = scr; - this->settings = settings; + this->host = host; this->header = header; - this->pl = pl; Panel_setHeader(super, "Categories"); for (size_t i = 0; i < ARRAYSIZE(categoriesPanelPages); i++) Panel_add(super, (Object*) ListItem_new(categoriesPanelPages[i].name, 0)); diff --git a/CategoriesPanel.h b/CategoriesPanel.h index 825cd0696..1f50b8a0f 100644 --- a/CategoriesPanel.h +++ b/CategoriesPanel.h @@ -8,23 +8,20 @@ in the source distribution for its full text. */ #include "Header.h" +#include "Machine.h" #include "Panel.h" -#include "ProcessList.h" #include "ScreenManager.h" -#include "Settings.h" typedef struct CategoriesPanel_ { Panel super; ScreenManager* scr; - - Settings* settings; + Machine* host; Header* header; - ProcessList* pl; } CategoriesPanel; extern const PanelClass CategoriesPanel_class; -CategoriesPanel* CategoriesPanel_new(ScreenManager* scr, Settings* settings, Header* header, ProcessList* pl); +CategoriesPanel* CategoriesPanel_new(ScreenManager* scr, Header* header, Machine* host); #endif diff --git a/ClockMeter.c b/ClockMeter.c index 8e3b66e64..ee712ae2d 100644 --- a/ClockMeter.c +++ b/ClockMeter.c @@ -22,10 +22,10 @@ static const int ClockMeter_attributes[] = { }; static void ClockMeter_updateValues(Meter* this) { - const ProcessList* pl = this->pl; + const Machine* host = this->host; struct tm result; - const struct tm* lt = localtime_r(&pl->realtime.tv_sec, &result); + const struct tm* lt = localtime_r(&host->realtime.tv_sec, &result); this->values[0] = lt->tm_hour * 60 + lt->tm_min; strftime(this->txtBuffer, sizeof(this->txtBuffer), "%H:%M:%S", lt); } diff --git a/CommandLine.c b/CommandLine.c index 9dd02dfc9..9e2018efe 100644 --- a/CommandLine.c +++ b/CommandLine.c @@ -277,18 +277,18 @@ static CommandLineStatus parseArguments(int argc, char** argv, CommandLineSettin return STATUS_OK; } -static void CommandLine_delay(ProcessList* pl, unsigned long millisec) { +static void CommandLine_delay(Machine* host, unsigned long millisec) { struct timespec req = { .tv_sec = 0, .tv_nsec = millisec * 1000000L }; while (nanosleep(&req, &req) == -1) continue; - Platform_gettime_realtime(&pl->realtime, &pl->realtimeMs); + Platform_gettime_realtime(&host->realtime, &host->realtimeMs); } static void setCommFilter(State* state, char** commFilter) { - ProcessList* pl = state->pl; + ProcessList* pl = state->host->pl; IncSet* inc = state->mainPanel->inc; IncSet_setFilter(inc, *commFilter); @@ -327,13 +327,14 @@ int CommandLine_run(int argc, char** argv) { if (!dc) dc = Hashtable_new(0, true); - ProcessList* pl = ProcessList_new(ut, flags.pidMatchList, flags.userId); + Machine* host = Machine_new(ut, flags.userId); + ProcessList* pl = ProcessList_new(host, flags.pidMatchList); + Settings* settings = Settings_new(host->activeCPUs, dm, dc); - Settings* settings = Settings_new(pl->activeCPUs, dm, dc); - pl->settings = settings; - - Header* header = Header_new(pl, settings, 2); + host->settings = settings; + Machine_addList(host, pl); + Header* header = Header_new(host, 2); Header_populateFromSettings(header); if (flags.delay != -1) @@ -367,9 +368,7 @@ int CommandLine_run(int argc, char** argv) { MainPanel_updateLabels(panel, settings->ss->treeView, flags.commFilter); State state = { - .settings = settings, - .ut = ut, - .pl = pl, + .host = host, .mainPanel = panel, .header = header, .pauseUpdate = false, @@ -381,11 +380,11 @@ int CommandLine_run(int argc, char** argv) { if (flags.commFilter) setCommFilter(&state, &(flags.commFilter)); - ScreenManager* scr = ScreenManager_new(header, settings, &state, true); + ScreenManager* scr = ScreenManager_new(header, host, &state, true); ScreenManager_add(scr, (Panel*) panel, -1); ProcessList_scan(pl, false); - CommandLine_delay(pl, 75); + CommandLine_delay(host, 75); ProcessList_scan(pl, false); if (settings->ss->allBranchesCollapsed) @@ -405,6 +404,7 @@ int CommandLine_run(int argc, char** argv) { Header_delete(header); ProcessList_delete(pl); + Machine_delete(host); ScreenManager_delete(scr); MetersPanel_cleanup(); diff --git a/DateMeter.c b/DateMeter.c index 96285963d..b38f43b0c 100644 --- a/DateMeter.c +++ b/DateMeter.c @@ -22,10 +22,10 @@ static const int DateMeter_attributes[] = { }; static void DateMeter_updateValues(Meter* this) { - const ProcessList* pl = this->pl; + const Machine* host = this->host; struct tm result; - const struct tm* lt = localtime_r(&pl->realtime.tv_sec, &result); + const struct tm* lt = localtime_r(&host->realtime.tv_sec, &result); this->values[0] = lt->tm_yday; int year = lt->tm_year + 1900; if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)) { diff --git a/DateTimeMeter.c b/DateTimeMeter.c index 1044ff39d..d46f3cb21 100644 --- a/DateTimeMeter.c +++ b/DateTimeMeter.c @@ -22,10 +22,10 @@ static const int DateTimeMeter_attributes[] = { }; static void DateTimeMeter_updateValues(Meter* this) { - const ProcessList* pl = this->pl; + const Machine* host = this->host; struct tm result; - const struct tm* lt = localtime_r(&pl->realtime.tv_sec, &result); + const struct tm* lt = localtime_r(&host->realtime.tv_sec, &result); int year = lt->tm_year + 1900; if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)) { this->total = 366; diff --git a/DiskIOMeter.c b/DiskIOMeter.c index adab8f732..545ec008a 100644 --- a/DiskIOMeter.c +++ b/DiskIOMeter.c @@ -32,10 +32,10 @@ static uint32_t cached_write_diff; static double cached_utilisation_diff; static void DiskIOMeter_updateValues(Meter* this) { - const ProcessList* pl = this->pl; + const Machine* host = this->host; static uint64_t cached_last_update; - uint64_t passedTimeInMs = pl->realtimeMs - cached_last_update; + uint64_t passedTimeInMs = host->realtimeMs - cached_last_update; /* update only every 500ms to have a sane span for rate calculation */ if (passedTimeInMs > 500) { @@ -55,7 +55,7 @@ static void DiskIOMeter_updateValues(Meter* this) { status = RATESTATUS_DATA; } - cached_last_update = pl->realtimeMs; + cached_last_update = host->realtimeMs; if (status == RATESTATUS_NODATA) { xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "no data"); diff --git a/DynamicMeter.c b/DynamicMeter.c index 40c06bb68..82e73a929 100644 --- a/DynamicMeter.c +++ b/DynamicMeter.c @@ -88,7 +88,7 @@ static void DynamicMeter_display(const Object* cast, RichString* out) { } static const char* DynamicMeter_getCaption(const Meter* this) { - const Settings* settings = this->pl->settings; + const Settings* settings = this->host->settings; const DynamicMeter* meter = Hashtable_get(settings->dynamicMeters, this->param); if (meter) return meter->caption ? meter->caption : meter->name; @@ -96,7 +96,7 @@ static const char* DynamicMeter_getCaption(const Meter* this) { } static void DynamicMeter_getUiName(const Meter* this, char* name, size_t length) { - const Settings* settings = this->pl->settings; + const Settings* settings = this->host->settings; const DynamicMeter* meter = Hashtable_get(settings->dynamicMeters, this->param); if (meter) { const char* uiName = meter->caption; diff --git a/Header.c b/Header.c index b2fc56cc9..3fafc70af 100644 --- a/Header.c +++ b/Header.c @@ -25,12 +25,11 @@ in the source distribution for its full text. #include "XUtils.h" -Header* Header_new(ProcessList* pl, Settings* settings, HeaderLayout hLayout) { +Header* Header_new(Machine* host, HeaderLayout hLayout) { Header* this = xCalloc(1, sizeof(Header)); this->columns = xMallocArray(HeaderLayout_getColumns(hLayout), sizeof(Vector*)); - this->settings = settings; - this->pl = pl; this->headerLayout = hLayout; + this->host = host; Header_forEachColumn(this, i) { this->columns[i] = Vector_new(Class(Meter), true, DEFAULT_SIZE); @@ -92,7 +91,8 @@ static void Header_addMeterByName(Header* this, const char* name, MeterModeId mo if ((end = strrchr(dynamic, ')')) == NULL) return; // htoprc parse failure *end = '\0'; - if (!DynamicMeter_search(this->settings->dynamicMeters, dynamic, ¶m)) + const Settings* settings = this->host->settings; + if (!DynamicMeter_search(settings->dynamicMeters, dynamic, ¶m)) return; // name lookup failure } else { param = 0; @@ -105,7 +105,7 @@ static void Header_addMeterByName(Header* this, const char* name, MeterModeId mo for (const MeterClass* const* type = Platform_meterTypes; *type; type++) { if (0 == strncmp(name, (*type)->name, nameLen) && (*type)->name[nameLen] == '\0') { - Meter* meter = Meter_new(this->pl, param, *type); + Meter* meter = Meter_new(this->host, param, *type); if (mode != 0) { Meter_setMode(meter, mode); } @@ -116,10 +116,11 @@ static void Header_addMeterByName(Header* this, const char* name, MeterModeId mo } void Header_populateFromSettings(Header* this) { - Header_setLayout(this, this->settings->hLayout); + const Settings* settings = this->host->settings; + Header_setLayout(this, settings->hLayout); Header_forEachColumn(this, col) { - const MeterColumnSetting* colSettings = &this->settings->hColumns[col]; + const MeterColumnSetting* colSettings = &settings->hColumns[col]; Vector_prune(this->columns[col]); for (size_t i = 0; i < colSettings->len; i++) { Header_addMeterByName(this, colSettings->names[i], colSettings->modes[i], col); @@ -130,7 +131,7 @@ void Header_populateFromSettings(Header* this) { } void Header_writeBackToSettings(const Header* this) { - Settings* settings = this->settings; + Settings* settings = this->host->settings; Settings_setHeaderLayout(settings, this->headerLayout); Header_forEachColumn(this, col) { @@ -172,7 +173,7 @@ Meter* Header_addMeterByClass(Header* this, const MeterClass* type, unsigned int Vector* meters = this->columns[column]; - Meter* meter = Meter_new(this->pl, param, type); + Meter* meter = Meter_new(this->host, param, type); Vector_add(meters, meter); return meter; } @@ -274,7 +275,8 @@ static int calcColumnWidthCount(const Header* this, const Meter* curMeter, const } int Header_calculateHeight(Header* this) { - const int pad = this->settings->headerMargin ? 2 : 0; + const Settings* settings = this->host->settings; + const int pad = settings->headerMargin ? 2 : 0; int maxHeight = pad; Header_forEachColumn(this, col) { @@ -295,7 +297,7 @@ int Header_calculateHeight(Header* this) { this->pad = pad; } - if (this->settings->screenTabs) { + if (settings->screenTabs) { maxHeight++; } diff --git a/Header.h b/Header.h index 954d434c3..add4d76bc 100644 --- a/Header.h +++ b/Header.h @@ -8,16 +8,14 @@ in the source distribution for its full text. */ #include "HeaderLayout.h" +#include "Machine.h" #include "Meter.h" -#include "ProcessList.h" -#include "Settings.h" #include "Vector.h" typedef struct Header_ { Vector** columns; - Settings* settings; - ProcessList* pl; + Machine* host; HeaderLayout headerLayout; int pad; int height; @@ -25,7 +23,7 @@ typedef struct Header_ { #define Header_forEachColumn(this_, i_) for (size_t (i_)=0, H_fEC_numColumns_ = HeaderLayout_getColumns((this_)->headerLayout); (i_) < H_fEC_numColumns_; ++(i_)) -Header* Header_new(ProcessList* pl, Settings* settings, HeaderLayout hLayout); +Header* Header_new(Machine *host, HeaderLayout hLayout); void Header_delete(Header* this); diff --git a/LoadAverageMeter.c b/LoadAverageMeter.c index 3fe3d9000..0beae04be 100644 --- a/LoadAverageMeter.c +++ b/LoadAverageMeter.c @@ -47,12 +47,12 @@ static void LoadAverageMeter_updateValues(Meter* this) { if (this->values[0] < 1.0) { this->curAttributes = OK_attributes; this->total = 1.0; - } else if (this->values[0] < this->pl->activeCPUs) { + } else if (this->values[0] < this->host->activeCPUs) { this->curAttributes = Medium_attributes; - this->total = this->pl->activeCPUs; + this->total = this->host->activeCPUs; } else { this->curAttributes = High_attributes; - this->total = 2 * this->pl->activeCPUs; + this->total = 2 * this->host->activeCPUs; } xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%.2f/%.2f/%.2f", this->values[0], this->values[1], this->values[2]); @@ -79,12 +79,12 @@ static void LoadMeter_updateValues(Meter* this) { if (this->values[0] < 1.0) { this->curAttributes = OK_attributes; this->total = 1.0; - } else if (this->values[0] < this->pl->activeCPUs) { + } else if (this->values[0] < this->host->activeCPUs) { this->curAttributes = Medium_attributes; - this->total = this->pl->activeCPUs; + this->total = this->host->activeCPUs; } else { this->curAttributes = High_attributes; - this->total = 2 * this->pl->activeCPUs; + this->total = 2 * this->host->activeCPUs; } xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%.2f", this->values[0]); diff --git a/Machine.c b/Machine.c new file mode 100644 index 000000000..63a996ef5 --- /dev/null +++ b/Machine.c @@ -0,0 +1,60 @@ +/* +htop - Machine.c +(C) 2023 Red Hat, Inc. +(C) 2004,2005 Hisham H. Muhammad +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "Machine.h" + +#include +#include + +#include "CRT.h" +#include "Hashtable.h" +#include "Macros.h" +#include "Platform.h" +#include "XUtils.h" + + +void Machine_init(Machine* this, UsersTable* usersTable, uid_t userId) { + this->usersTable = usersTable; + this->userId = userId; + + // always maintain valid realtime timestamps + Platform_gettime_realtime(&this->realtime, &this->realtimeMs); + +#ifdef HAVE_LIBHWLOC + this->topologyOk = false; + if (hwloc_topology_init(&this->topology) == 0) { + this->topologyOk = + #if HWLOC_API_VERSION < 0x00020000 + /* try to ignore the top-level machine object type */ + 0 == hwloc_topology_ignore_type_keep_structure(this->topology, HWLOC_OBJ_MACHINE) && + /* ignore caches, which don't add structure */ + 0 == hwloc_topology_ignore_type_keep_structure(this->topology, HWLOC_OBJ_CORE) && + 0 == hwloc_topology_ignore_type_keep_structure(this->topology, HWLOC_OBJ_CACHE) && + 0 == hwloc_topology_set_flags(this->topology, HWLOC_TOPOLOGY_FLAG_WHOLE_SYSTEM) && + #else + 0 == hwloc_topology_set_all_types_filter(this->topology, HWLOC_TYPE_FILTER_KEEP_STRUCTURE) && + #endif + 0 == hwloc_topology_load(this->topology); + } +#endif +} + +void Machine_done(Machine* this) { +#ifdef HAVE_LIBHWLOC + if (this->topologyOk) { + hwloc_topology_destroy(this->topology); + } +#else + (void)this; +#endif +} + +void Machine_addList(Machine* this, struct ProcessList_ *pl) { + // currently only process lists are supported + this->pl = pl; +} diff --git a/Machine.h b/Machine.h new file mode 100644 index 000000000..3683701a3 --- /dev/null +++ b/Machine.h @@ -0,0 +1,89 @@ +#ifndef HEADER_Machine +#define HEADER_Machine +/* +htop - Machine.h +(C) 2023 Red Hat, Inc. +(C) 2004,2005 Hisham H. Muhammad +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "config.h" // IWYU pragma: keep + +#include +#include +#include +#include +#include + +#include "Hashtable.h" +#include "Settings.h" +#include "UsersTable.h" +#include "Vector.h" + +#ifdef HAVE_LIBHWLOC +#include +#endif + + +#ifndef MAX_NAME +#define MAX_NAME 128 +#endif + +#ifndef MAX_READ +#define MAX_READ 2048 +#endif + +typedef unsigned long long int memory_t; +#define MEMORY_MAX ULLONG_MAX + +struct ProcessList_; + +typedef struct Machine_ { + Settings* settings; + + struct timeval realtime; /* time of the current sample */ + uint64_t realtimeMs; /* current time in milliseconds */ + uint64_t monotonicMs; /* same, but from monotonic clock */ + + #ifdef HAVE_LIBHWLOC + hwloc_topology_t topology; + bool topologyOk; + #endif + + memory_t totalMem; + memory_t usedMem; + memory_t buffersMem; + memory_t cachedMem; + memory_t sharedMem; + memory_t availableMem; + + memory_t totalSwap; + memory_t usedSwap; + memory_t cachedSwap; + + unsigned int activeCPUs; + unsigned int existingCPUs; + + UsersTable* usersTable; + uid_t userId; + + /* To become an array of lists - processes, cgroups, filesystems,... etc */ + /* for now though, just point back to the one list we have at the moment */ + struct ProcessList_ *pl; +} Machine; + + +Machine* Machine_new(UsersTable* usersTable, uid_t userId); + +void Machine_init(Machine* this, UsersTable* usersTable, uid_t userId); + +void Machine_delete(Machine* this); + +void Machine_done(Machine* this); + +bool Machine_isCPUonline(const Machine* this, unsigned int id); + +void Machine_addList(Machine* this, struct ProcessList_ *pl); + +#endif diff --git a/MainPanel.c b/MainPanel.c index 0889a5c20..14bd3bbdf 100644 --- a/MainPanel.c +++ b/MainPanel.c @@ -53,10 +53,9 @@ static const char* MainPanel_getValue(Panel* this, int i) { static HandlerResult MainPanel_eventHandler(Panel* super, int ch) { MainPanel* this = (MainPanel*) super; - - HandlerResult result = IGNORED; - + Machine* host = this->state->host; Htop_Reaction reaction = HTOP_OK; + HandlerResult result = IGNORED; /* Let supervising ScreenManager handle resize */ if (ch == KEY_RESIZE) @@ -66,20 +65,19 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) { bool needReset = ch != ERR; #ifdef HAVE_GETMOUSE /* except mouse events while mouse support is disabled */ - if (!(ch != KEY_MOUSE || this->state->settings->enableMouse)) + if (!(ch != KEY_MOUSE || host->settings->enableMouse)) needReset = false; #endif if (needReset) this->state->hideSelection = false; - Settings* settings = this->state->settings; + Settings* settings = host->settings; ScreenSettings* ss = settings->ss; if (EVENT_IS_HEADER_CLICK(ch)) { int x = EVENT_HEADER_CLICK_GET_X(ch); - const ProcessList* pl = this->state->pl; int hx = super->scrollH + x + 1; - ProcessField field = ProcessList_keyAt(pl, hx); + ProcessField field = ProcessList_keyAt(host->pl, hx); if (ss->treeView && ss->treeViewAlwaysByPID) { ss->treeView = false; ss->direction = 1; @@ -98,7 +96,7 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) { } else if (ch != ERR && this->inc->active) { bool filterChanged = IncSet_handleKey(this->inc, ch, super, MainPanel_getValue, NULL); if (filterChanged) { - this->state->pl->incFilter = IncSet_filter(this->inc); + host->pl->incFilter = IncSet_filter(this->inc); reaction = HTOP_REFRESH | HTOP_REDRAW_BAR; } if (this->inc->found) { @@ -123,7 +121,7 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) { } if ((reaction & HTOP_REDRAW_BAR) == HTOP_REDRAW_BAR) { - MainPanel_updateLabels(this, settings->ss->treeView, this->state->pl->incFilter); + MainPanel_updateLabels(this, settings->ss->treeView, host->pl->incFilter); } if ((reaction & HTOP_RESIZE) == HTOP_RESIZE) { result |= RESIZE; @@ -138,13 +136,13 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) { result |= RESCAN; } if ((reaction & HTOP_SAVE_SETTINGS) == HTOP_SAVE_SETTINGS) { - this->state->settings->changed = true; + host->settings->changed = true; } if ((reaction & HTOP_QUIT) == HTOP_QUIT) { return BREAK_LOOP; } if ((reaction & HTOP_KEEP_FOLLOWING) != HTOP_KEEP_FOLLOWING) { - this->state->pl->following = -1; + host->pl->following = -1; Panel_setSelectionColor(super, PANEL_SELECTION_FOCUS); } return result; @@ -197,7 +195,8 @@ static void MainPanel_drawFunctionBar(Panel* super, bool hideFunctionBar) { static void MainPanel_printHeader(Panel* super) { MainPanel* this = (MainPanel*) super; - ProcessList_printHeader(this->state->pl, &super->header); + Machine* host = this->state->host; + ProcessList_printHeader(host->pl, &super->header); } const PanelClass MainPanel_class = { diff --git a/Makefile.am b/Makefile.am index 427342014..94db4d77e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -60,6 +60,7 @@ myhtopsources = \ InfoScreen.c \ ListItem.c \ LoadAverageMeter.c \ + Machine.c \ MainPanel.c \ MemoryMeter.c \ MemorySwapMeter.c \ @@ -122,6 +123,7 @@ myhtopheaders = \ InfoScreen.h \ ListItem.h \ LoadAverageMeter.h \ + Machine.h \ Macros.h \ MainPanel.h \ MemoryMeter.h \ diff --git a/MemorySwapMeter.c b/MemorySwapMeter.c index 46b2a6b73..814841f70 100644 --- a/MemorySwapMeter.c +++ b/MemorySwapMeter.c @@ -53,9 +53,9 @@ static void MemorySwapMeter_init(Meter* this) { } if (!data->memoryMeter) - data->memoryMeter = Meter_new(this->pl, 0, (const MeterClass*) Class(MemoryMeter)); + data->memoryMeter = Meter_new(this->host, 0, (const MeterClass*) Class(MemoryMeter)); if (!data->swapMeter) - data->swapMeter = Meter_new(this->pl, 0, (const MeterClass*) Class(SwapMeter)); + data->swapMeter = Meter_new(this->host, 0, (const MeterClass*) Class(SwapMeter)); if (Meter_initFn(data->memoryMeter)) Meter_init(data->memoryMeter); diff --git a/Meter.c b/Meter.c index a88de04a3..cf0fe36ac 100644 --- a/Meter.c +++ b/Meter.c @@ -32,12 +32,12 @@ const MeterClass Meter_class = { } }; -Meter* Meter_new(const struct ProcessList_* pl, unsigned int param, const MeterClass* type) { +Meter* Meter_new(const Machine* host, unsigned int param, const MeterClass* type) { Meter* this = xCalloc(1, sizeof(Meter)); Object_setClass(this, type); this->h = 1; this->param = param; - this->pl = pl; + this->host = host; this->curItems = type->maxItems; this->curAttributes = NULL; this->values = type->maxItems ? xCalloc(type->maxItems, sizeof(double)) : NULL; @@ -294,7 +294,7 @@ static const char* const GraphMeterMode_dotsAscii[] = { }; static void GraphMeterMode_draw(Meter* this, int x, int y, int w) { - const ProcessList* pl = this->pl; + const Machine* host = this->host; if (!this->drawData) { this->drawData = xCalloc(1, sizeof(GraphData)); @@ -322,10 +322,10 @@ static void GraphMeterMode_draw(Meter* this, int x, int y, int w) { x += captionLen; w -= captionLen; - if (!timercmp(&pl->realtime, &(data->time), <)) { - int globalDelay = this->pl->settings->delay; + if (!timercmp(&host->realtime, &(data->time), <)) { + int globalDelay = this->host->settings->delay; struct timeval delay = { .tv_sec = globalDelay / 10, .tv_usec = (globalDelay % 10) * 100000L }; - timeradd(&pl->realtime, &delay, &(data->time)); + timeradd(&host->realtime, &delay, &(data->time)); for (int i = 0; i < nValues - 1; i++) data->values[i] = data->values[i + 1]; diff --git a/Meter.h b/Meter.h index 8e8618d66..db93e4c04 100644 --- a/Meter.h +++ b/Meter.h @@ -15,8 +15,8 @@ in the source distribution for its full text. #include #include "ListItem.h" +#include "Machine.h" #include "Object.h" -#include "ProcessList.h" #define METER_TXTBUFFER_LEN 256 @@ -105,6 +105,7 @@ typedef struct GraphData_ { struct Meter_ { Object super; Meter_Draw draw; + const Machine* host; char* caption; int mode; @@ -112,7 +113,6 @@ struct Meter_ { GraphData* drawData; int h; int columnWidthCount; /**< only used internally by the Header */ - const ProcessList* pl; uint8_t curItems; const int* curAttributes; char txtBuffer[METER_TXTBUFFER_LEN]; @@ -145,7 +145,7 @@ typedef enum { extern const MeterClass Meter_class; -Meter* Meter_new(const ProcessList* pl, unsigned int param, const MeterClass* type); +Meter* Meter_new(const Machine* host, unsigned int param, const MeterClass* type); int Meter_humanUnit(char* buffer, unsigned long int value, size_t size); diff --git a/NetworkIOMeter.c b/NetworkIOMeter.c index d41eafa89..5945bae79 100644 --- a/NetworkIOMeter.c +++ b/NetworkIOMeter.c @@ -26,10 +26,10 @@ static uint32_t cached_txb_diff; static uint32_t cached_txp_diff; static void NetworkIOMeter_updateValues(Meter* this) { - const ProcessList* pl = this->pl; + const Machine* host = this->host; static uint64_t cached_last_update = 0; - uint64_t passedTimeInMs = pl->realtimeMs - cached_last_update; + uint64_t passedTimeInMs = host->realtimeMs - cached_last_update; /* update only every 500ms to have a sane span for rate calculation */ if (passedTimeInMs > 500) { @@ -50,7 +50,7 @@ static void NetworkIOMeter_updateValues(Meter* this) { status = RATESTATUS_DATA; } - cached_last_update = pl->realtimeMs; + cached_last_update = host->realtimeMs; if (status == RATESTATUS_NODATA) { xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "no data"); diff --git a/Process.c b/Process.c index c92d01b99..43f7f70dd 100644 --- a/Process.c +++ b/Process.c @@ -407,7 +407,7 @@ static inline char* stpcpyWithNewlineConversion(char* dstStr, const char* srcStr */ void Process_makeCommandStr(Process* this) { ProcessMergedCommand* mc = &this->mergedCommand; - const Settings* settings = this->settings; + const Settings* settings = this->host->settings; bool showMergedCommand = settings->showMergedCommand; bool showProgramPath = settings->showProgramPath; @@ -675,15 +675,16 @@ void Process_writeCommand(const Process* this, int attr, int baseAttr, RichStrin int strStart = RichString_size(str); - const bool highlightBaseName = this->settings->highlightBaseName; + const Settings* settings = this->host->settings; + const bool highlightBaseName = settings->highlightBaseName; const bool highlightSeparator = true; - const bool highlightDeleted = this->settings->highlightDeletedExe; + const bool highlightDeleted = settings->highlightDeletedExe; if (!mergedCommand) { int len = 0; const char* cmdline = this->cmdline; - if (highlightBaseName || !this->settings->showProgramPath) { + if (highlightBaseName || !settings->showProgramPath) { int basename = 0; for (int i = 0; i < this->cmdlineBasenameEnd; i++) { if (cmdline[i] == '/') { @@ -694,7 +695,7 @@ void Process_writeCommand(const Process* this, int attr, int baseAttr, RichStrin } } if (len == 0) { - if (this->settings->showProgramPath) { + if (settings->showProgramPath) { strStart += basename; } else { cmdline += basename; @@ -705,7 +706,7 @@ void Process_writeCommand(const Process* this, int attr, int baseAttr, RichStrin RichString_appendWide(str, attr, cmdline); - if (this->settings->highlightBaseName) { + if (settings->highlightBaseName) { RichString_setAttrn(str, baseAttr, strStart, len); } @@ -833,16 +834,17 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field char buffer[256]; size_t n = sizeof(buffer); int attr = CRT_colors[DEFAULT_COLOR]; - bool coloring = this->settings->highlightMegabytes; + const Settings* settings = this->host->settings; + bool coloring = settings->highlightMegabytes; switch (field) { case COMM: { int baseattr = CRT_colors[PROCESS_BASENAME]; - if (this->settings->highlightThreads && Process_isThread(this)) { + if (settings->highlightThreads && Process_isThread(this)) { attr = CRT_colors[PROCESS_THREAD]; baseattr = CRT_colors[PROCESS_THREAD_BASENAME]; } - const ScreenSettings* ss = this->settings->ss; + const ScreenSettings* ss = settings->ss; if (!ss->treeView || this->indent == 0) { Process_writeCommand(this, attr, baseattr, str); return; @@ -890,7 +892,7 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field const char* procExe; if (this->procExe) { attr = CRT_colors[Process_isUserlandThread(this) ? PROCESS_THREAD_BASENAME : PROCESS_BASENAME]; - if (this->settings->highlightDeletedExe) { + if (settings->highlightDeletedExe) { if (this->procExeDeleted) attr = CRT_colors[FAILED_READ]; else if (this->usesDeletedLib) @@ -920,7 +922,7 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field return; } case ELAPSED: { - const uint64_t rt = this->processList->realtimeMs; + const uint64_t rt = this->host->realtimeMs; const uint64_t st = this->starttime_ctime * 1000; const uint64_t dt = rt < st ? 0 : @@ -946,7 +948,7 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field break; case PERCENT_CPU: Process_printPercentage(this->percent_cpu, buffer, n, Process_fieldWidths[PERCENT_CPU], &attr); break; case PERCENT_NORM_CPU: { - float cpuPercentage = this->percent_cpu / this->processList->activeCPUs; + float cpuPercentage = this->percent_cpu / this->host->activeCPUs; Process_printPercentage(cpuPercentage, buffer, n, Process_fieldWidths[PERCENT_CPU], &attr); break; } @@ -960,7 +962,7 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field else xSnprintf(buffer, n, "%3ld ", this->priority); break; - case PROCESSOR: xSnprintf(buffer, n, "%3d ", Settings_cpuId(this->settings, this->processor)); break; + case PROCESSOR: xSnprintf(buffer, n, "%3d ", Settings_cpuId(settings, this->processor)); break; case SCHEDULERPOLICY: { const char* schedPolStr = "N/A"; #ifdef SCHEDULER_SUPPORT @@ -1044,11 +1046,12 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field void Process_display(const Object* cast, RichString* out) { const Process* this = (const Process*) cast; - const ProcessField* fields = this->settings->ss->fields; + const Settings* settings = this->host->settings; + const ProcessField* fields = settings->ss->fields; for (int i = 0; fields[i]; i++) As_Process(this)->writeField(this, out, fields[i]); - if (this->settings->shadowOtherUsers && this->st_uid != Process_getuid) { + if (settings->shadowOtherUsers && this->st_uid != Process_getuid) { RichString_setAttr(out, CRT_colors[PROCESS_SHADOW]); } @@ -1056,7 +1059,7 @@ void Process_display(const Object* cast, RichString* out) { RichString_setAttr(out, CRT_colors[PROCESS_TAG]); } - if (this->settings->highlightChanges) { + if (settings->highlightChanges) { if (Process_isTomb(this)) { out->highlightAttr = CRT_colors[PROCESS_TOMB]; } else if (Process_isNew(this)) { @@ -1081,7 +1084,8 @@ void Process_done(Process* this) { * happens on what is displayed - whether comm, full path, basename, etc.. So * this follows Process_writeField(COMM) and Process_writeCommand */ const char* Process_getCommand(const Process* this) { - if ((Process_isUserlandThread(this) && this->settings->showThreadNames) || !this->mergedCommand.str) { + const Settings* settings = this->host->settings; + if ((Process_isUserlandThread(this) && settings->showThreadNames) || !this->mergedCommand.str) { return this->cmdline; } @@ -1098,8 +1102,8 @@ const ProcessClass Process_class = { .writeField = Process_writeField, }; -void Process_init(Process* this, const Settings* settings) { - this->settings = settings; +void Process_init(Process* this, const Machine* host) { + this->host = host; this->tag = false; this->showChildren = true; this->show = true; @@ -1117,9 +1121,11 @@ void Process_toggleTag(Process* this) { } bool Process_isNew(const Process* this) { - assert(this->processList); - if (this->processList->monotonicMs >= this->seenStampMs) { - return this->processList->monotonicMs - this->seenStampMs <= 1000 * (uint64_t)this->processList->settings->highlightDelaySecs; + assert(this->host); + const Machine* host = this->host; + if (host->monotonicMs >= this->seenStampMs) { + const Settings* settings = host->settings; + return host->monotonicMs - this->seenStampMs <= 1000 * (uint64_t)settings->highlightDelaySecs; } return false; } @@ -1153,7 +1159,7 @@ int Process_compare(const void* v1, const void* v2) { const Process* p1 = (const Process*)v1; const Process* p2 = (const Process*)v2; - const Settings* settings = p1->settings; + const Settings* settings = p1->host->settings; const ScreenSettings* ss = settings->ss; ProcessField key = ScreenSettings_getActiveSortKey(ss); diff --git a/Process.h b/Process.h index db32ecc17..ad89fd452 100644 --- a/Process.h +++ b/Process.h @@ -82,7 +82,7 @@ typedef enum ProcessState_ { SLEEPING } ProcessState; -struct Settings_; +struct Machine_; /* Holds information about regions of the cmdline that should be * highlighted (e.g. program basename, delimiter, comm). */ @@ -108,9 +108,8 @@ typedef struct Process_ { /* Super object for emulated OOP */ Object super; - /* Pointer to quasi-global data structures */ - const struct ProcessList_* processList; - const struct Settings_* settings; + /* Pointer to quasi-global data */ + const struct Machine_* host; /* Process identifier */ pid_t pid; @@ -305,7 +304,7 @@ extern uint8_t Process_fieldWidths[LAST_PROCESSFIELD]; extern int Process_pidDigits; extern int Process_uidDigits; -typedef Process* (*Process_New)(const struct Settings_*); +typedef Process* (*Process_New)(const struct Machine_*); typedef void (*Process_WriteField)(const Process*, RichString*, ProcessField); typedef int (*Process_CompareByKey)(const Process*, const Process*, ProcessField); @@ -389,7 +388,7 @@ void Process_done(Process* this); extern const ProcessClass Process_class; -void Process_init(Process* this, const struct Settings_* settings); +void Process_init(Process* this, const struct Machine_* host); void Process_toggleTag(Process* this); diff --git a/ProcessList.c b/ProcessList.c index 8e989d02f..49217b8c7 100644 --- a/ProcessList.c +++ b/ProcessList.c @@ -20,58 +20,18 @@ in the source distribution for its full text. #include "XUtils.h" -ProcessList* ProcessList_init(ProcessList* this, const ObjectClass* klass, UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) { +void ProcessList_init(ProcessList* this, const ObjectClass* klass, Machine* host, Hashtable* pidMatchList) { this->processes = Vector_new(klass, true, DEFAULT_SIZE); this->displayList = Vector_new(klass, false, DEFAULT_SIZE); - this->processTable = Hashtable_new(200, false); - this->needsSort = true; - - this->usersTable = usersTable; this->pidMatchList = pidMatchList; - - this->userId = userId; - - // set later by platform-specific code - this->activeCPUs = 0; - this->existingCPUs = 0; - this->monotonicMs = 0; - - // always maintain valid realtime timestamps - Platform_gettime_realtime(&this->realtime, &this->realtimeMs); - -#ifdef HAVE_LIBHWLOC - this->topologyOk = false; - if (hwloc_topology_init(&this->topology) == 0) { - this->topologyOk = - #if HWLOC_API_VERSION < 0x00020000 - /* try to ignore the top-level machine object type */ - 0 == hwloc_topology_ignore_type_keep_structure(this->topology, HWLOC_OBJ_MACHINE) && - /* ignore caches, which don't add structure */ - 0 == hwloc_topology_ignore_type_keep_structure(this->topology, HWLOC_OBJ_CORE) && - 0 == hwloc_topology_ignore_type_keep_structure(this->topology, HWLOC_OBJ_CACHE) && - 0 == hwloc_topology_set_flags(this->topology, HWLOC_TOPOLOGY_FLAG_WHOLE_SYSTEM) && - #else - 0 == hwloc_topology_set_all_types_filter(this->topology, HWLOC_TYPE_FILTER_KEEP_STRUCTURE) && - #endif - 0 == hwloc_topology_load(this->topology); - } -#endif - + this->needsSort = true; this->following = -1; - - return this; + this->host = host; } void ProcessList_done(ProcessList* this) { -#ifdef HAVE_LIBHWLOC - if (this->topologyOk) { - hwloc_topology_destroy(this->topology); - } -#endif - Hashtable_delete(this->processTable); - Vector_delete(this->displayList); Vector_delete(this->processes); } @@ -134,7 +94,7 @@ static const char* ProcessField_alignedTitle(const Settings* settings, ProcessFi void ProcessList_printHeader(const ProcessList* this, RichString* header) { RichString_rewind(header, RichString_size(header)); - const Settings* settings = this->settings; + const Settings* settings = this->host->settings; const ScreenSettings* ss = settings->ss; const ProcessField* fields = ss->fields; @@ -168,10 +128,9 @@ void ProcessList_printHeader(const ProcessList* this, RichString* header) { void ProcessList_add(ProcessList* this, Process* p) { assert(Vector_indexOf(this->processes, p, Process_pidEqualCompare) == -1); assert(Hashtable_get(this->processTable, p->pid) == NULL); - p->processList = this; // highlighting processes found in first scan by first scan marked "far in the past" - p->seenStampMs = this->monotonicMs; + p->seenStampMs = this->host->monotonicMs; Vector_add(this->processes, p); Hashtable_put(this->processTable, p->pid, p); @@ -325,7 +284,7 @@ static void ProcessList_buildTree(ProcessList* this) { } void ProcessList_updateDisplayList(ProcessList* this) { - if (this->settings->ss->treeView) { + if (this->host->settings->ss->treeView) { if (this->needsSort) ProcessList_buildTree(this); } else { @@ -341,7 +300,7 @@ void ProcessList_updateDisplayList(ProcessList* this) { ProcessField ProcessList_keyAt(const ProcessList* this, int at) { int x = 0; - const Settings* settings = this->settings; + const Settings* settings = this->host->settings; const ProcessField* fields = settings->ss->fields; ProcessField field; for (int i = 0; (field = fields[i]); i++) { @@ -387,7 +346,8 @@ void ProcessList_rebuildPanel(ProcessList* this) { Panel_prune(this->panel); /* Follow main process if followed a userland thread and threads are now hidden */ - const Settings* settings = this->settings; + const Machine* host= this->host; + const Settings* settings = host->settings; if (this->following != -1 && settings->hideUserlandThreads) { const Process* followedProcess = (const Process*) Hashtable_get(this->processTable, this->following); if (followedProcess && Process_isThread(followedProcess) && Hashtable_get(this->processTable, followedProcess->tgid) != NULL) { @@ -403,7 +363,7 @@ void ProcessList_rebuildPanel(ProcessList* this) { Process* p = (Process*) Vector_get(this->displayList, i); if ( (!p->show) - || (this->userId != (uid_t) -1 && (p->st_uid != this->userId)) + || (host->userId != (uid_t) -1 && (p->st_uid != host->userId)) || (incFilter && !(String_contains_i(Process_getCommand(p), incFilter, true))) || (this->pidMatchList && !Hashtable_get(this->pidMatchList, p->tgid)) ) continue; @@ -443,7 +403,7 @@ Process* ProcessList_getProcess(ProcessList* this, pid_t pid, bool* preExisting, assert(Vector_indexOf(this->processes, proc, Process_pidEqualCompare) != -1); assert(proc->pid == pid); } else { - proc = constructor(this->settings); + proc = constructor(this->host); assert(proc->cmdline == NULL); proc->pid = pid; } @@ -474,16 +434,18 @@ void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate) { // set scan timestamp static bool firstScanDone = false; + Machine* host = this->host; if (firstScanDone) { - Platform_gettime_monotonic(&this->monotonicMs); + Platform_gettime_monotonic(&host->monotonicMs); } else { - this->monotonicMs = 0; + host->monotonicMs = 0; firstScanDone = true; } ProcessList_goThroughEntries(this, false); uid_t maxUid = 0; + const Settings* settings = host->settings; for (int i = Vector_size(this->processes) - 1; i >= 0; i--) { Process* p = (Process*) Vector_get(this->processes, i); Process_makeCommandStr(p); @@ -494,14 +456,14 @@ void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate) { if (p->tombStampMs > 0) { // remove tombed process - if (this->monotonicMs >= p->tombStampMs) { + if (host->monotonicMs >= p->tombStampMs) { ProcessList_removeIndex(this, p, i); } } else if (p->updated == false) { // process no longer exists - if (this->settings->highlightChanges && p->wasShown) { + if (settings->highlightChanges && p->wasShown) { // mark tombed - p->tombStampMs = this->monotonicMs + 1000 * this->settings->highlightDelaySecs; + p->tombStampMs = host->monotonicMs + 1000 * settings->highlightDelaySecs; } else { // immediately remove ProcessList_removeIndex(this, p, i); diff --git a/ProcessList.h b/ProcessList.h index eab122a3e..d09cc0723 100644 --- a/ProcessList.h +++ b/ProcessList.h @@ -16,6 +16,7 @@ in the source distribution for its full text. #include #include "Hashtable.h" +#include "Machine.h" #include "Object.h" #include "Panel.h" #include "Process.h" @@ -24,76 +25,34 @@ in the source distribution for its full text. #include "UsersTable.h" #include "Vector.h" -#ifdef HAVE_LIBHWLOC -#include -#endif - - -#ifndef MAX_NAME -#define MAX_NAME 128 -#endif - -#ifndef MAX_READ -#define MAX_READ 2048 -#endif - -typedef unsigned long long int memory_t; -#define MEMORY_MAX ULLONG_MAX typedef struct ProcessList_ { - const Settings* settings; + struct Machine_* host; Vector* processes; /* all known processes; sort order can vary and differ from display order */ Vector* displayList; /* process tree flattened in display order (borrowed); updated in ProcessList_updateDisplayList when rebuilding panel */ Hashtable* processTable; /* fast known process lookup by PID */ - UsersTable* usersTable; bool needsSort; - struct timeval realtime; /* time of the current sample */ - uint64_t realtimeMs; /* current time in milliseconds */ - uint64_t monotonicMs; /* same, but from monotonic clock */ - Panel* panel; int following; - uid_t userId; const char* incFilter; Hashtable* pidMatchList; - #ifdef HAVE_LIBHWLOC - hwloc_topology_t topology; - bool topologyOk; - #endif - unsigned int totalTasks; unsigned int runningTasks; unsigned int userlandThreads; unsigned int kernelThreads; - - memory_t totalMem; - memory_t usedMem; - memory_t buffersMem; - memory_t cachedMem; - memory_t sharedMem; - memory_t availableMem; - - memory_t totalSwap; - memory_t usedSwap; - memory_t cachedSwap; - - unsigned int activeCPUs; - unsigned int existingCPUs; } ProcessList; /* Implemented by platforms */ -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId); +ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList); void ProcessList_delete(ProcessList* pl); void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate); -bool ProcessList_isCPUonline(const ProcessList* super, unsigned int id); - -ProcessList* ProcessList_init(ProcessList* this, const ObjectClass* klass, UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId); +void ProcessList_init(ProcessList* this, const ObjectClass* klass, Machine* host, Hashtable* pidMatchList); void ProcessList_done(ProcessList* this); diff --git a/ScreenManager.c b/ScreenManager.c index 6b9fada51..f1897893e 100644 --- a/ScreenManager.c +++ b/ScreenManager.c @@ -24,7 +24,7 @@ in the source distribution for its full text. #include "XUtils.h" -ScreenManager* ScreenManager_new(Header* header, const Settings* settings, State* state, bool owner) { +ScreenManager* ScreenManager_new(Header* header, Machine* host, State* state, bool owner) { ScreenManager* this; this = xMalloc(sizeof(ScreenManager)); this->x1 = 0; @@ -34,7 +34,7 @@ ScreenManager* ScreenManager_new(Header* header, const Settings* settings, State this->panels = Vector_new(Class(Panel), owner, DEFAULT_SIZE); this->panelCount = 0; this->header = header; - this->settings = settings; + this->host = host; this->state = state; this->allowFocusChange = true; return this; @@ -116,12 +116,12 @@ void ScreenManager_resize(ScreenManager* this) { } static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTimeout, bool* redraw, bool* rescan, bool* timedOut, bool* force_redraw) { - ProcessList* pl = this->header->pl; + Machine* host = this->host; - Platform_gettime_realtime(&pl->realtime, &pl->realtimeMs); - double newTime = ((double)pl->realtime.tv_sec * 10) + ((double)pl->realtime.tv_usec / 100000); + Platform_gettime_realtime(&host->realtime, &host->realtimeMs); + double newTime = ((double)host->realtime.tv_sec * 10) + ((double)host->realtime.tv_usec / 100000); - *timedOut = (newTime - *oldTime > this->settings->delay); + *timedOut = (newTime - *oldTime > host->settings->delay); *rescan |= *timedOut; if (newTime < *oldTime) { @@ -131,12 +131,12 @@ static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTi if (*rescan) { *oldTime = newTime; int oldUidDigits = Process_uidDigits; - if (!this->state->pauseUpdate && (*sortTimeout == 0 || this->settings->ss->treeView)) { - pl->needsSort = true; + if (!this->state->pauseUpdate && (*sortTimeout == 0 || host->settings->ss->treeView)) { + host->pl->needsSort = true; *sortTimeout = 1; } // scan processes first - some header values are calculated there - ProcessList_scan(pl, this->state->pauseUpdate); + ProcessList_scan(host->pl, this->state->pauseUpdate); // always update header, especially to avoid gaps in graph meters Header_updateData(this->header); // force redraw if the number of UID digits was changed @@ -146,7 +146,7 @@ static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTi *redraw = true; } if (*redraw) { - ProcessList_rebuildPanel(pl); + ProcessList_rebuildPanel(host->pl); if (!this->state->hideMeters) Header_draw(this->header); } @@ -175,8 +175,9 @@ static inline bool drawTab(const int* y, int* x, int l, const char* name, bool c } static void ScreenManager_drawScreenTabs(ScreenManager* this) { - ScreenSettings** screens = this->settings->screens; - int cur = this->settings->ssIndex; + Settings* settings = this->host->settings; + ScreenSettings** screens = settings->screens; + int cur = settings->ssIndex; int l = COLS; Panel* panel = (Panel*) Vector_get(this->panels, 0); int y = panel->y - 1; @@ -197,7 +198,8 @@ static void ScreenManager_drawScreenTabs(ScreenManager* this) { } static void ScreenManager_drawPanels(ScreenManager* this, int focus, bool force_redraw) { - if (this->settings->screenTabs) { + Settings* settings = this->host->settings; + if (settings->screenTabs) { ScreenManager_drawScreenTabs(this); } const int nPanels = this->panelCount; @@ -217,6 +219,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey, con int focus = 0; Panel* panelFocus = (Panel*) Vector_get(this->panels, focus); + Settings* settings = this->host->settings; double oldTime = 0.0; @@ -247,7 +250,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey, con HandlerResult result = IGNORED; #ifdef HAVE_GETMOUSE - if (ch == KEY_MOUSE && this->settings->enableMouse) { + if (ch == KEY_MOUSE && settings->enableMouse) { ch = ERR; MEVENT mevent; int ok = getmouse(&mevent); @@ -262,7 +265,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey, con if (mevent.y == panel->y) { ch = EVENT_HEADER_CLICK(mevent.x - panel->x); break; - } else if (this->settings->screenTabs && mevent.y == panel->y - 1) { + } else if (settings->screenTabs && mevent.y == panel->y - 1) { ch = EVENT_SCREEN_TAB_CLICK(mevent.x); break; } else if (mevent.y > panel->y && mevent.y <= panel->y + panel->h) { diff --git a/ScreenManager.h b/ScreenManager.h index d08a9413f..37821bb4f 100644 --- a/ScreenManager.h +++ b/ScreenManager.h @@ -11,8 +11,8 @@ in the source distribution for its full text. #include "Action.h" #include "Header.h" +#include "Machine.h" #include "Panel.h" -#include "Settings.h" #include "Vector.h" @@ -25,12 +25,12 @@ typedef struct ScreenManager_ { const char* name; int panelCount; Header* header; - const Settings* settings; + Machine* host; State* state; bool allowFocusChange; } ScreenManager; -ScreenManager* ScreenManager_new(Header* header, const Settings* settings, State* state, bool owner); +ScreenManager* ScreenManager_new(Header* header, Machine* host, State* state, bool owner); void ScreenManager_delete(ScreenManager* this); diff --git a/TasksMeter.c b/TasksMeter.c index 64c9837c8..b5563fcab 100644 --- a/TasksMeter.c +++ b/TasksMeter.c @@ -24,19 +24,20 @@ static const int TasksMeter_attributes[] = { }; static void TasksMeter_updateValues(Meter* this) { - const ProcessList* pl = this->pl; + const Machine* host = this->host; + const ProcessList* pl = host->pl; this->values[0] = pl->kernelThreads; this->values[1] = pl->userlandThreads; this->values[2] = pl->totalTasks - pl->kernelThreads - pl->userlandThreads; - this->values[3] = MINIMUM(pl->runningTasks, pl->activeCPUs); + this->values[3] = MINIMUM(pl->runningTasks, host->activeCPUs); this->total = pl->totalTasks; - xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%u/%u", MINIMUM(pl->runningTasks, pl->activeCPUs), pl->totalTasks); + xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%u/%u", MINIMUM(pl->runningTasks, host->activeCPUs), pl->totalTasks); } static void TasksMeter_display(const Object* cast, RichString* out) { const Meter* this = (const Meter*)cast; - const Settings* settings = this->pl->settings; + const Settings* settings = this->host->settings; char buffer[20]; int len; diff --git a/darwin/DarwinProcess.c b/darwin/DarwinProcess.c index 6027c25be..22004e36b 100644 --- a/darwin/DarwinProcess.c +++ b/darwin/DarwinProcess.c @@ -51,10 +51,10 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = { [TRANSLATED] = { .name = "TRANSLATED", .title = "T ", .description = "Translation info (T translated, N native)", .flags = 0, }, }; -Process* DarwinProcess_new(const Settings* settings) { +Process* DarwinProcess_new(const Machine* host) { DarwinProcess* this = xCalloc(1, sizeof(DarwinProcess)); Object_setClass(this, Class(DarwinProcess)); - Process_init(&this->super, settings); + Process_init(&this->super, host); this->utime = 0; this->stime = 0; @@ -291,6 +291,7 @@ static char* DarwinProcess_getDevname(dev_t dev) { void DarwinProcess_setFromKInfoProc(Process* proc, const struct kinfo_proc* ps, bool exists) { DarwinProcess* dp = (DarwinProcess*)proc; + const Settings* settings = proc->host->settings; const struct extern_proc* ep = &ps->kp_proc; @@ -328,7 +329,7 @@ void DarwinProcess_setFromKInfoProc(Process* proc, const struct kinfo_proc* ps, DarwinProcess_updateExe(ep->p_pid, proc); DarwinProcess_updateCmdLine(ps, proc); - if (proc->settings->ss->flags & PROCESS_FLAG_CWD) { + if (settings->ss->flags & PROCESS_FLAG_CWD) { DarwinProcess_updateCwd(ep->p_pid, proc); } } @@ -341,7 +342,7 @@ void DarwinProcess_setFromKInfoProc(Process* proc, const struct kinfo_proc* ps, * To mitigate this we only fetch TTY information if the TTY * field is enabled in the settings. */ - if (proc->settings->ss->flags & PROCESS_FLAG_TTY) { + if (settings->ss->flags & PROCESS_FLAG_TTY) { proc->tty_name = DarwinProcess_getDevname(proc->tty_nr); if (!proc->tty_name) { /* devname failed: prevent us from calling it again */ diff --git a/darwin/DarwinProcess.h b/darwin/DarwinProcess.h index bd1797460..89a0576da 100644 --- a/darwin/DarwinProcess.h +++ b/darwin/DarwinProcess.h @@ -9,7 +9,7 @@ in the source distribution for its full text. #include -#include "Settings.h" +#include "Machine.h" #include "darwin/DarwinProcessList.h" @@ -28,7 +28,7 @@ extern const ProcessClass DarwinProcess_class; extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD]; -Process* DarwinProcess_new(const Settings* settings); +Process* DarwinProcess_new(const Machine* settings); void Process_delete(Object* cast); diff --git a/darwin/DarwinProcessList.c b/darwin/DarwinProcessList.c index 6e9eecf7c..16c44d2cb 100644 --- a/darwin/DarwinProcessList.c +++ b/darwin/DarwinProcessList.c @@ -89,15 +89,15 @@ static struct kinfo_proc* ProcessList_getKInfoProcs(size_t* count) { CRT_fatalError("Unable to get kinfo_procs"); } -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) { +ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList) { DarwinProcessList* this = xCalloc(1, sizeof(DarwinProcessList)); - ProcessList_init(&this->super, Class(DarwinProcess), usersTable, pidMatchList, userId); + ProcessList_init(&this->super, Class(DarwinProcess), host, pidMatchList); /* Initialize the CPU information */ - this->super.activeCPUs = ProcessList_allocateCPULoadInfo(&this->prev_load); + host->activeCPUs = ProcessList_allocateCPULoadInfo(&this->prev_load); // TODO: support offline CPUs and hot swapping - this->super.existingCPUs = this->super.activeCPUs; + host->existingCPUs = host->activeCPUs; ProcessList_getHostInfo(&this->host_info); ProcessList_allocateCPULoadInfo(&this->curr_load); @@ -123,6 +123,7 @@ void ProcessList_delete(ProcessList* this) { void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { DarwinProcessList* dpl = (DarwinProcessList*)super; + const Machine* host = super->host; bool preExisting = true; struct kinfo_proc* ps; size_t count; @@ -142,13 +143,13 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { /* Get the time difference */ dpl->global_diff = 0; - for (unsigned int i = 0; i < dpl->super.existingCPUs; ++i) { + for (unsigned int i = 0; i < host->existingCPUs; ++i) { for (size_t j = 0; j < CPU_STATE_MAX; ++j) { dpl->global_diff += dpl->curr_load[i].cpu_ticks[j] - dpl->prev_load[i].cpu_ticks[j]; } } - const double time_interval_ns = Platform_schedulerTicksToNanoseconds(dpl->global_diff) / (double) dpl->super.activeCPUs; + const double time_interval_ns = Platform_schedulerTicksToNanoseconds(dpl->global_diff) / (double) host->activeCPUs; /* Clear the thread counts */ super->kernelThreads = 0; @@ -173,7 +174,7 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { if (proc->super.st_uid != ps[i].kp_eproc.e_ucred.cr_uid) { proc->super.st_uid = ps[i].kp_eproc.e_ucred.cr_uid; - proc->super.user = UsersTable_getRef(super->usersTable, proc->super.st_uid); + proc->super.user = UsersTable_getRef(host->usersTable, proc->super.st_uid); } // Disabled for High Sierra due to bug in macOS High Sierra @@ -193,11 +194,21 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { free(ps); } -bool ProcessList_isCPUonline(const ProcessList* super, unsigned int id) { - assert(id < super->existingCPUs); +Machine* Machine_new(UsersTable* usersTable, uid_t userId) { + Machine* this = xCalloc(1, sizeof(Machine)); + Machine_init(this, usersTable, userId); + return this; +} + +void Machine_delete(Machine* host) { + free(host); +} + +bool Machine_isCPUonline(const Machine* host, unsigned int id) { + assert(id < host->existingCPUs); // TODO: support offline CPUs and hot swapping - (void) super; (void) id; + (void) host; (void) id; return true; } diff --git a/darwin/DarwinProcessList.h b/darwin/DarwinProcessList.h index ec504bcfe..128896aec 100644 --- a/darwin/DarwinProcessList.h +++ b/darwin/DarwinProcessList.h @@ -28,12 +28,16 @@ typedef struct DarwinProcessList_ { ZfsArcStats zfs; } DarwinProcessList; -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId); +ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList); void ProcessList_delete(ProcessList* this); void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate); -bool ProcessList_isCPUonline(const ProcessList* super, unsigned int id); +Machine* Machine_new(UsersTable* usersTable, uid_t userId); + +bool Machine_isCPUonline(const Machine* host, unsigned int id); + +void Machine_delete(Machine* host); #endif diff --git a/darwin/Platform.c b/darwin/Platform.c index 20bfec26e..71d082479 100644 --- a/darwin/Platform.c +++ b/darwin/Platform.c @@ -233,13 +233,13 @@ int Platform_getMaxPid(void) { } static double Platform_setCPUAverageValues(Meter* mtr) { - const ProcessList* dpl = mtr->pl; - unsigned int activeCPUs = dpl->activeCPUs; + const Machine* host = mtr->host; + unsigned int activeCPUs = host->activeCPUs; double sumNice = 0.0; double sumNormal = 0.0; double sumKernel = 0.0; double sumPercent = 0.0; - for (unsigned int i = 1; i <= dpl->existingCPUs; i++) { + for (unsigned int i = 1; i <= host->existingCPUs; i++) { sumPercent += Platform_setCPUValues(mtr, i); sumNice += mtr->values[CPU_METER_NICE]; sumNormal += mtr->values[CPU_METER_NORMAL]; @@ -257,7 +257,7 @@ double Platform_setCPUValues(Meter* mtr, unsigned int cpu) { return Platform_setCPUAverageValues(mtr); } - const DarwinProcessList* dpl = (const DarwinProcessList*)mtr->pl; + const DarwinProcessList* dpl = (const DarwinProcessList*)mtr->host->pl; const processor_cpu_load_info_t prev = &dpl->prev_load[cpu - 1]; const processor_cpu_load_info_t curr = &dpl->curr_load[cpu - 1]; double total = 0; @@ -286,7 +286,8 @@ double Platform_setCPUValues(Meter* mtr, unsigned int cpu) { } void Platform_setMemoryValues(Meter* mtr) { - const DarwinProcessList* dpl = (const DarwinProcessList*)mtr->pl; + const Machine* host = mtr->host; + const DarwinProcessList* dpl = (const DarwinProcessList*) host->pl; const struct vm_statistics* vm = &dpl->vm_stats; double page_K = (double)vm_page_size / (double)1024; @@ -312,13 +313,13 @@ void Platform_setSwapValues(Meter* mtr) { } void Platform_setZfsArcValues(Meter* this) { - const DarwinProcessList* dpl = (const DarwinProcessList*) this->pl; + const DarwinProcessList* dpl = (const DarwinProcessList*) this->host->pl; ZfsArcMeter_readStats(this, &(dpl->zfs)); } void Platform_setZfsCompressedArcValues(Meter* this) { - const DarwinProcessList* dpl = (const DarwinProcessList*) this->pl; + const DarwinProcessList* dpl = (const DarwinProcessList*) this->host->pl; ZfsCompressedArcMeter_readStats(this, &(dpl->zfs)); } diff --git a/dragonflybsd/DragonFlyBSDProcess.c b/dragonflybsd/DragonFlyBSDProcess.c index 7ff924511..7cfc71be5 100644 --- a/dragonflybsd/DragonFlyBSDProcess.c +++ b/dragonflybsd/DragonFlyBSDProcess.c @@ -52,10 +52,10 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = { [JAIL] = { .name = "JAIL", .title = "JAIL ", .description = "Jail prison name", .flags = 0, }, }; -Process* DragonFlyBSDProcess_new(const Settings* settings) { +Process* DragonFlyBSDProcess_new(const Machine* host) { DragonFlyBSDProcess* this = xCalloc(1, sizeof(DragonFlyBSDProcess)); Object_setClass(this, Class(DragonFlyBSDProcess)); - Process_init(&this->super, settings); + Process_init(&this->super, host); return &this->super; } diff --git a/dragonflybsd/DragonFlyBSDProcess.h b/dragonflybsd/DragonFlyBSDProcess.h index e0a77ef17..92747b1ac 100644 --- a/dragonflybsd/DragonFlyBSDProcess.h +++ b/dragonflybsd/DragonFlyBSDProcess.h @@ -12,7 +12,7 @@ in the source distribution for its full text. #include "Object.h" #include "Process.h" -#include "Settings.h" +#include "Machine.h" typedef struct DragonFlyBSDProcess_ { @@ -25,7 +25,7 @@ extern const ProcessClass DragonFlyBSDProcess_class; extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD]; -Process* DragonFlyBSDProcess_new(const Settings* settings); +Process* DragonFlyBSDProcess_new(const Machine* host); void Process_delete(Object* cast); diff --git a/dragonflybsd/DragonFlyBSDProcessList.c b/dragonflybsd/DragonFlyBSDProcessList.c index 39b052507..c66917fb6 100644 --- a/dragonflybsd/DragonFlyBSDProcessList.c +++ b/dragonflybsd/DragonFlyBSDProcessList.c @@ -42,12 +42,12 @@ static int MIB_kern_cp_time[2]; static int MIB_kern_cp_times[2]; static int kernelFScale; -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) { +ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList) { size_t len; char errbuf[_POSIX2_LINE_MAX]; DragonFlyBSDProcessList* dfpl = xCalloc(1, sizeof(DragonFlyBSDProcessList)); ProcessList* pl = (ProcessList*) dfpl; - ProcessList_init(pl, Class(DragonFlyBSDProcess), usersTable, pidMatchList, userId); + ProcessList_init(pl, Class(DragonFlyBSDProcess), host, pidMatchList); // physical memory in system: hw.physmem // physical page size: hw.pagesize @@ -95,15 +95,15 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, ui sysctl(MIB_kern_cp_times, 2, dfpl->cp_times_o, &len, NULL, 0); } - pl->existingCPUs = MAXIMUM(cpus, 1); + host->existingCPUs = MAXIMUM(cpus, 1); // TODO: support offline CPUs and hot swapping - pl->activeCPUs = pl->existingCPUs; + host->activeCPUs = host->existingCPUs; if (cpus == 1 ) { dfpl->cpus = xRealloc(dfpl->cpus, sizeof(CPUData)); } else { // on smp we need CPUs + 1 to store averages too (as kernel kindly provides that as well) - dfpl->cpus = xRealloc(dfpl->cpus, (pl->existingCPUs + 1) * sizeof(CPUData)); + dfpl->cpus = xRealloc(dfpl->cpus, (host->existingCPUs + 1) * sizeof(CPUData)); } len = sizeof(kernelFScale); @@ -420,7 +420,8 @@ static char* DragonFlyBSDProcessList_readJailName(DragonFlyBSDProcessList* dfpl, void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { DragonFlyBSDProcessList* dfpl = (DragonFlyBSDProcessList*) super; - const Settings* settings = super->settings; + const Machine* host = super->host; + const Settings* settings = host->settings; bool hideKernelThreads = settings->hideKernelThreads; bool hideUserlandThreads = settings->hideUserlandThreads; @@ -467,7 +468,7 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { proc->processor = kproc->kp_lwp.kl_origcpu; proc->starttime_ctime = kproc->kp_start.tv_sec; Process_fillStarttimeBuffer(proc); - proc->user = UsersTable_getRef(super->usersTable, proc->st_uid); + proc->user = UsersTable_getRef(host->usersTable, proc->st_uid); proc->tty_nr = kproc->kp_tdev; // control terminal device number const char* name = (kproc->kp_tdev != NODEV) ? devname(kproc->kp_tdev, S_IFCHR) : NULL; @@ -499,7 +500,7 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { proc->ppid = kproc->kp_ppid; if (proc->st_uid != kproc->kp_uid) { // some processes change users (eg. to lower privs) proc->st_uid = kproc->kp_uid; - proc->user = UsersTable_getRef(super->usersTable, proc->st_uid); + proc->user = UsersTable_getRef(host->usersTable, proc->st_uid); } if (settings->updateProcessNames) { DragonFlyBSDProcessList_updateProcessName(dfpl->kd, kproc, proc); @@ -605,11 +606,21 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { } } -bool ProcessList_isCPUonline(const ProcessList* super, unsigned int id) { - assert(id < super->existingCPUs); +Machine* Machine_new(UsersTable* usersTable, uid_t userId) { + Machine* this = xCalloc(1, sizeof(Machine)); + Machine_init(this, usersTable, userId); + return this; +} + +void Machine_delete(Machine* host) { + free(host); +} + +bool Machine_isCPUonline(const Machine* host, unsigned int id) { + assert(id < host->existingCPUs); // TODO: support offline CPUs and hot swapping - (void) super; (void) id; + (void) host; (void) id; return true; } diff --git a/dragonflybsd/DragonFlyBSDProcessList.h b/dragonflybsd/DragonFlyBSDProcessList.h index 2cc400545..406722e03 100644 --- a/dragonflybsd/DragonFlyBSDProcessList.h +++ b/dragonflybsd/DragonFlyBSDProcessList.h @@ -53,12 +53,16 @@ typedef struct DragonFlyBSDProcessList_ { Hashtable* jails; } DragonFlyBSDProcessList; -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId); +ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList); void ProcessList_delete(ProcessList* this); void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate); -bool ProcessList_isCPUonline(const ProcessList* super, unsigned int id); +Machine* Machine_new(UsersTable* usersTable, uid_t userId); + +bool Machine_isCPUonline(const Machine* host, unsigned int id); + +void Machine_delete(Machine* host); #endif diff --git a/dragonflybsd/Platform.c b/dragonflybsd/Platform.c index 995276421..1c0ef0a40 100644 --- a/dragonflybsd/Platform.c +++ b/dragonflybsd/Platform.c @@ -172,8 +172,9 @@ int Platform_getMaxPid(void) { } double Platform_setCPUValues(Meter* this, unsigned int cpu) { - const DragonFlyBSDProcessList* fpl = (const DragonFlyBSDProcessList*) this->pl; - unsigned int cpus = this->pl->activeCPUs; + const Machine* host = this->host; + const DragonFlyBSDProcessList* fpl = (const DragonFlyBSDProcessList*) host->pl; + unsigned int cpus = this->host->activeCPUs; const CPUData* cpuData; if (cpus == 1) { @@ -188,7 +189,7 @@ double Platform_setCPUValues(Meter* this, unsigned int cpu) { v[CPU_METER_NICE] = cpuData->nicePercent; v[CPU_METER_NORMAL] = cpuData->userPercent; - if (this->pl->settings->detailedCPUTime) { + if (host->settings->detailedCPUTime) { v[CPU_METER_KERNEL] = cpuData->systemPercent; v[CPU_METER_IRQ] = cpuData->irqPercent; this->curItems = 4; @@ -209,7 +210,7 @@ double Platform_setCPUValues(Meter* this, unsigned int cpu) { void Platform_setMemoryValues(Meter* this) { // TODO - const ProcessList* pl = this->pl; + const ProcessList* pl = this->host->pl; this->total = pl->totalMem; this->values[MEMORY_METER_USED] = pl->usedMem; @@ -221,7 +222,7 @@ void Platform_setMemoryValues(Meter* this) { } void Platform_setSwapValues(Meter* this) { - const ProcessList* pl = this->pl; + const ProcessList* pl = this->host->pl; this->total = pl->totalSwap; this->values[SWAP_METER_USED] = pl->usedSwap; // mtr->values[SWAP_METER_CACHE] = "pages that are both in swap and RAM, like SwapCached on linux" diff --git a/freebsd/FreeBSDProcess.c b/freebsd/FreeBSDProcess.c index 90bc1f2c3..4970ff2cc 100644 --- a/freebsd/FreeBSDProcess.c +++ b/freebsd/FreeBSDProcess.c @@ -56,10 +56,10 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = { [EMULATION] = { .name = "EMULATION", .title = "EMULATION ", .description = "System call emulation environment (ABI)", .flags = 0, }, }; -Process* FreeBSDProcess_new(const Settings* settings) { +Process* FreeBSDProcess_new(const Machine* machine) { FreeBSDProcess* this = xCalloc(1, sizeof(FreeBSDProcess)); Object_setClass(this, Class(FreeBSDProcess)); - Process_init(&this->super, settings); + Process_init(&this->super, machine); return &this->super; } diff --git a/freebsd/FreeBSDProcess.h b/freebsd/FreeBSDProcess.h index 654f9cfaf..012cfa278 100644 --- a/freebsd/FreeBSDProcess.h +++ b/freebsd/FreeBSDProcess.h @@ -11,7 +11,7 @@ in the source distribution for its full text. #include "Object.h" #include "Process.h" -#include "Settings.h" +#include "Machine.h" typedef struct FreeBSDProcess_ { @@ -25,7 +25,7 @@ extern const ProcessClass FreeBSDProcess_class; extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD]; -Process* FreeBSDProcess_new(const Settings* settings); +Process* FreeBSDProcess_new(const Machine* host); void Process_delete(Object* cast); diff --git a/freebsd/FreeBSDProcessList.c b/freebsd/FreeBSDProcessList.c index f545a3eee..bd6e3aacd 100644 --- a/freebsd/FreeBSDProcessList.c +++ b/freebsd/FreeBSDProcessList.c @@ -59,12 +59,12 @@ static int MIB_kern_cp_time[2]; static int MIB_kern_cp_times[2]; static int kernelFScale; -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) { +ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList) { size_t len; char errbuf[_POSIX2_LINE_MAX]; FreeBSDProcessList* fpl = xCalloc(1, sizeof(FreeBSDProcessList)); ProcessList* pl = (ProcessList*) fpl; - ProcessList_init(pl, Class(FreeBSDProcess), usersTable, pidMatchList, userId); + ProcessList_init(pl, Class(FreeBSDProcess), host, pidMatchList); // physical memory in system: hw.physmem // physical page size: hw.pagesize @@ -129,15 +129,15 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, ui sysctl(MIB_kern_cp_times, 2, fpl->cp_times_o, &len, NULL, 0); } - pl->existingCPUs = MAXIMUM(cpus, 1); + host->existingCPUs = MAXIMUM(cpus, 1); // TODO: support offline CPUs and hot swapping - pl->activeCPUs = pl->existingCPUs; + host->activeCPUs = host->existingCPUs; if (cpus == 1 ) { fpl->cpus = xRealloc(fpl->cpus, sizeof(CPUData)); } else { // on smp we need CPUs + 1 to store averages too (as kernel kindly provides that as well) - fpl->cpus = xRealloc(fpl->cpus, (pl->existingCPUs + 1) * sizeof(CPUData)); + fpl->cpus = xRealloc(fpl->cpus, (host->existingCPUs + 1) * sizeof(CPUData)); } @@ -174,8 +174,9 @@ void ProcessList_delete(ProcessList* this) { static inline void FreeBSDProcessList_scanCPU(ProcessList* pl) { const FreeBSDProcessList* fpl = (FreeBSDProcessList*) pl; + const Machine* host = pl->host; - unsigned int cpus = pl->existingCPUs; // actual CPU count + unsigned int cpus = host->existingCPUs; // actual CPU count unsigned int maxcpu = cpus; // max iteration (in case we have average + smp) int cp_times_offset; @@ -260,7 +261,7 @@ static inline void FreeBSDProcessList_scanCPU(ProcessList* pl) { continue; // TODO: test with hyperthreading and multi-cpu systems - if (pl->settings->showCPUTemperature) { + if (host->settings->showCPUTemperature) { int temperature; size_t len = sizeof(temperature); char mibBuffer[32]; @@ -271,7 +272,7 @@ static inline void FreeBSDProcessList_scanCPU(ProcessList* pl) { } // TODO: test with hyperthreading and multi-cpu systems - if (pl->settings->showCPUFrequency) { + if (host->settings->showCPUFrequency) { int frequency; size_t len = sizeof(frequency); char mibBuffer[32]; @@ -285,7 +286,7 @@ static inline void FreeBSDProcessList_scanCPU(ProcessList* pl) { // calculate max temperature and avg frequency for average meter and // propagate frequency to all cores if only supplied for CPU 0 if (cpus > 1) { - if (pl->settings->showCPUTemperature) { + if (host->settings->showCPUTemperature) { double maxTemp = NAN; for (unsigned int i = 1; i < maxcpu; i++) { const double coreTemp = fpl->cpus[i].temperature; @@ -298,7 +299,7 @@ static inline void FreeBSDProcessList_scanCPU(ProcessList* pl) { fpl->cpus[0].temperature = maxTemp; } - if (pl->settings->showCPUFrequency) { + if (host->settings->showCPUFrequency) { const double coreZeroFreq = fpl->cpus[1].frequency; double freqSum = coreZeroFreq; if (!isnan(coreZeroFreq)) { @@ -317,6 +318,7 @@ static inline void FreeBSDProcessList_scanCPU(ProcessList* pl) { static inline void FreeBSDProcessList_scanMemoryInfo(ProcessList* pl) { FreeBSDProcessList* fpl = (FreeBSDProcessList*) pl; + Machine* host = pl->host; // @etosan: // memory counter relationships seem to be these: @@ -338,12 +340,12 @@ static inline void FreeBSDProcessList_scanMemoryInfo(ProcessList* pl) { //disabled for now, as it is always smaller than phycal amount of memory... //...to avoid "where is my memory?" questions - //sysctl(MIB_vm_stats_vm_v_page_count, 4, &(pl->totalMem), &len, NULL, 0); - //pl->totalMem *= pageSizeKb; + //sysctl(MIB_vm_stats_vm_v_page_count, 4, &(host->totalMem), &len, NULL, 0); + //host->totalMem *= pageSizeKb; len = sizeof(totalMem); sysctl(MIB_hw_physmem, 2, &(totalMem), &len, NULL, 0); totalMem /= 1024; - pl->totalMem = totalMem; + host->totalMem = totalMem; len = sizeof(memActive); sysctl(MIB_vm_stats_vm_v_active_count, 4, &(memActive), &len, NULL, 0); @@ -358,29 +360,29 @@ static inline void FreeBSDProcessList_scanMemoryInfo(ProcessList* pl) { len = sizeof(buffersMem); sysctl(MIB_vfs_bufspace, 2, &(buffersMem), &len, NULL, 0); buffersMem /= 1024; - pl->buffersMem = buffersMem; + host->buffersMem = buffersMem; len = sizeof(cachedMem); sysctl(MIB_vm_stats_vm_v_cache_count, 4, &(cachedMem), &len, NULL, 0); cachedMem *= pageSizeKb; - pl->cachedMem = cachedMem; + host->cachedMem = cachedMem; len = sizeof(vmtotal); sysctl(MIB_vm_vmtotal, 2, &(vmtotal), &len, NULL, 0); - pl->sharedMem = vmtotal.t_rmshr * pageSizeKb; + host->sharedMem = vmtotal.t_rmshr * pageSizeKb; - pl->usedMem = fpl->memActive + fpl->memWire; + host->usedMem = fpl->memActive + fpl->memWire; struct kvm_swap swap[16]; int nswap = kvm_getswapinfo(fpl->kd, swap, ARRAYSIZE(swap), 0); - pl->totalSwap = 0; - pl->usedSwap = 0; + host->totalSwap = 0; + host->usedSwap = 0; for (int i = 0; i < nswap; i++) { - pl->totalSwap += swap[i].ksw_total; - pl->usedSwap += swap[i].ksw_used; + host->totalSwap += swap[i].ksw_total; + host->usedSwap += swap[i].ksw_used; } - pl->totalSwap *= pageSizeKb; - pl->usedSwap *= pageSizeKb; + host->totalSwap *= pageSizeKb; + host->usedSwap *= pageSizeKb; } static void FreeBSDProcessList_updateExe(const struct kinfo_proc* kproc, Process* proc) { @@ -483,7 +485,8 @@ IGNORE_WCASTQUAL_END void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { FreeBSDProcessList* fpl = (FreeBSDProcessList*) super; - const Settings* settings = super->settings; + const Machine* host = super->host; + const Settings* settings = host->settings; bool hideKernelThreads = settings->hideKernelThreads; bool hideUserlandThreads = settings->hideUserlandThreads; @@ -518,10 +521,10 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { proc->st_uid = kproc->ki_uid; proc->starttime_ctime = kproc->ki_start.tv_sec; if (proc->starttime_ctime < 0) { - proc->starttime_ctime = super->realtimeMs / 1000; + proc->starttime_ctime = super->host.realtimeMs / 1000; } Process_fillStarttimeBuffer(proc); - proc->user = UsersTable_getRef(super->usersTable, proc->st_uid); + proc->user = UsersTable_getRef(host->usersTable, proc->st_uid); ProcessList_add(super, proc); FreeBSDProcessList_updateExe(kproc, proc); @@ -553,7 +556,7 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { if (proc->st_uid != kproc->ki_uid) { // some processes change users (eg. to lower privs) proc->st_uid = kproc->ki_uid; - proc->user = UsersTable_getRef(super->usersTable, proc->st_uid); + proc->user = UsersTable_getRef(host->usersTable, proc->st_uid); } if (settings->updateProcessNames) { FreeBSDProcessList_updateProcessName(fpl->kd, kproc, proc); @@ -569,7 +572,7 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { proc->time = (kproc->ki_runtime + 5000) / 10000; proc->percent_cpu = 100.0 * ((double)kproc->ki_pctcpu / (double)kernelFScale); - proc->percent_mem = 100.0 * proc->m_resident / (double)(super->totalMem); + proc->percent_mem = 100.0 * proc->m_resident / (double)(host->totalMem); Process_updateCPUFieldWidths(proc->percent_cpu); if (kproc->ki_stat == SRUN && kproc->ki_oncpu != NOCPU) { @@ -621,11 +624,21 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { } } -bool ProcessList_isCPUonline(const ProcessList* super, unsigned int id) { - assert(id < super->existingCPUs); +Machine* Machine_new(UsersTable* usersTable, uid_t userId) { + Machine* this = xCalloc(1, sizeof(Machine)); + Machine_init(this, usersTable, userId); + return this; +} + +void Machine_delete(Machine* host) { + free(host); +} + +bool Machine_isCPUonline(const Machine* host, unsigned int id) { + assert(id < host->existingCPUs); // TODO: support offline CPUs and hot swapping - (void) super; (void) id; + (void) host; (void) id; return true; } diff --git a/freebsd/FreeBSDProcessList.h b/freebsd/FreeBSDProcessList.h index 98510fadc..b7e29f8b1 100644 --- a/freebsd/FreeBSDProcessList.h +++ b/freebsd/FreeBSDProcessList.h @@ -47,12 +47,16 @@ typedef struct FreeBSDProcessList_ { } FreeBSDProcessList; -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId); +ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList); void ProcessList_delete(ProcessList* this); void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate); -bool ProcessList_isCPUonline(const ProcessList* super, unsigned int id); +Machine* Machine_new(UsersTable* usersTable, uid_t userId); + +bool Machine_isCPUonline(const Machine* host, unsigned int id); + +void Machine_delete(Machine* host); #endif diff --git a/freebsd/Platform.c b/freebsd/Platform.c index cab80929b..3ba2778ee 100644 --- a/freebsd/Platform.c +++ b/freebsd/Platform.c @@ -193,8 +193,9 @@ int Platform_getMaxPid(void) { } double Platform_setCPUValues(Meter* this, unsigned int cpu) { - const FreeBSDProcessList* fpl = (const FreeBSDProcessList*) this->pl; - unsigned int cpus = this->pl->activeCPUs; + const Machine* host = this->host; + const FreeBSDProcessList* fpl = (const FreeBSDProcessList*) host->pl; + unsigned int cpus = host->activeCPUs; const CPUData* cpuData; if (cpus == 1) { @@ -209,7 +210,7 @@ double Platform_setCPUValues(Meter* this, unsigned int cpu) { v[CPU_METER_NICE] = cpuData->nicePercent; v[CPU_METER_NORMAL] = cpuData->userPercent; - if (this->pl->settings->detailedCPUTime) { + if (host->settings->detailedCPUTime) { v[CPU_METER_KERNEL] = cpuData->systemPercent; v[CPU_METER_IRQ] = cpuData->irqPercent; this->curItems = 4; @@ -229,15 +230,16 @@ double Platform_setCPUValues(Meter* this, unsigned int cpu) { } void Platform_setMemoryValues(Meter* this) { - const ProcessList* pl = this->pl; + const Machine* host = this->host; + const ProcessList* pl = host->pl; const FreeBSDProcessList* fpl = (const FreeBSDProcessList*) pl; - this->total = pl->totalMem; - this->values[MEMORY_METER_USED] = pl->usedMem; - this->values[MEMORY_METER_BUFFERS] = pl->buffersMem; - this->values[MEMORY_METER_SHARED] = pl->sharedMem; + this->total = host->totalMem; + this->values[MEMORY_METER_USED] = host->usedMem; + this->values[MEMORY_METER_BUFFERS] = host->buffersMem; + this->values[MEMORY_METER_SHARED] = host->sharedMem; // this->values[MEMORY_METER_COMPRESSED] = "compressed memory, like zswap on linux" - this->values[MEMORY_METER_CACHE] = pl->cachedMem; + this->values[MEMORY_METER_CACHE] = host->cachedMem; // this->values[MEMORY_METER_AVAILABLE] = "available memory" if (fpl->zfs.enabled) { @@ -252,21 +254,22 @@ void Platform_setMemoryValues(Meter* this) { } void Platform_setSwapValues(Meter* this) { - const ProcessList* pl = this->pl; - this->total = pl->totalSwap; - this->values[SWAP_METER_USED] = pl->usedSwap; + const Machine* host = this->host; + + this->total = host->totalSwap; + this->values[SWAP_METER_USED] = host->usedSwap; // this->values[SWAP_METER_CACHE] = "pages that are both in swap and RAM, like SwapCached on linux" // this->values[SWAP_METER_FRONTSWAP] = "pages that are accounted to swap but stored elsewhere, like frontswap on linux" } void Platform_setZfsArcValues(Meter* this) { - const FreeBSDProcessList* fpl = (const FreeBSDProcessList*) this->pl; + const FreeBSDProcessList* fpl = (const FreeBSDProcessList*) this->host->pl; ZfsArcMeter_readStats(this, &(fpl->zfs)); } void Platform_setZfsCompressedArcValues(Meter* this) { - const FreeBSDProcessList* fpl = (const FreeBSDProcessList*) this->pl; + const FreeBSDProcessList* fpl = (const FreeBSDProcessList*) this->host->pl; ZfsCompressedArcMeter_readStats(this, &(fpl->zfs)); } diff --git a/linux/HugePageMeter.c b/linux/HugePageMeter.c index 1efde2f2f..0e4294443 100644 --- a/linux/HugePageMeter.c +++ b/linux/HugePageMeter.c @@ -43,7 +43,7 @@ static void HugePageMeter_updateValues(Meter* this) { memory_t usedTotal = 0; unsigned nextUsed = 0; - const LinuxProcessList* lpl = (const LinuxProcessList*) this->pl; + const LinuxProcessList* lpl = (const LinuxProcessList*) this->host->pl; this->total = lpl->totalHugePageMem; this->values[0] = 0; HugePageMeter_active_labels[0] = " used:"; diff --git a/linux/LinuxProcess.c b/linux/LinuxProcess.c index 8f9c34620..a3a4c8218 100644 --- a/linux/LinuxProcess.c +++ b/linux/LinuxProcess.c @@ -106,10 +106,10 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = { #endif }; -Process* LinuxProcess_new(const Settings* settings) { +Process* LinuxProcess_new(const Machine* host) { LinuxProcess* this = xCalloc(1, sizeof(LinuxProcess)); Object_setClass(this, Class(LinuxProcess)); - Process_init(&this->super, settings); + Process_init(&this->super, host); return &this->super; } @@ -197,7 +197,7 @@ bool LinuxProcess_changeAutogroupPriorityBy(Process* this, Arg delta) { static void LinuxProcess_writeField(const Process* this, RichString* str, ProcessField field) { const LinuxProcess* lp = (const LinuxProcess*) this; - bool coloring = this->settings->highlightMegabytes; + bool coloring = this->host->settings->highlightMegabytes; char buffer[256]; buffer[255] = '\0'; int attr = CRT_colors[DEFAULT_COLOR]; size_t n = sizeof(buffer) - 1; diff --git a/linux/LinuxProcess.h b/linux/LinuxProcess.h index 3e5d38040..6c309893c 100644 --- a/linux/LinuxProcess.h +++ b/linux/LinuxProcess.h @@ -14,9 +14,9 @@ in the source distribution for its full text. #include #include "linux/IOPriority.h" +#include "Machine.h" #include "Object.h" #include "Process.h" -#include "Settings.h" #define PROCESS_FLAG_LINUX_IOPRIO 0x00000100 @@ -118,7 +118,7 @@ extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD]; extern const ProcessClass LinuxProcess_class; -Process* LinuxProcess_new(const Settings* settings); +Process* LinuxProcess_new(const Machine* host); void Process_delete(Object* cast); diff --git a/linux/LinuxProcessList.c b/linux/LinuxProcessList.c index 2ecdefc34..911fc3e1e 100644 --- a/linux/LinuxProcessList.c +++ b/linux/LinuxProcessList.c @@ -216,6 +216,7 @@ static void LinuxProcessList_updateCPUcount(ProcessList* super) { * https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/unix/sysv/linux/getsysstats.c;hb=HEAD */ + Machine* host = super->host; LinuxProcessList* this = (LinuxProcessList*) super; unsigned int existing = 0, active = 0; @@ -224,15 +225,15 @@ static void LinuxProcessList_updateCPUcount(ProcessList* super) { this->cpuData = xCalloc(2, sizeof(CPUData)); this->cpuData[0].online = true; /* average is always "online" */ this->cpuData[1].online = true; - super->activeCPUs = 1; - super->existingCPUs = 1; + host->activeCPUs = 1; + host->existingCPUs = 1; } DIR* dir = opendir("/sys/devices/system/cpu"); if (!dir) return; - unsigned int currExisting = super->existingCPUs; + unsigned int currExisting = host->existingCPUs; const struct dirent* entry; while ((entry = readdir(dir)) != NULL) { @@ -288,20 +289,20 @@ static void LinuxProcessList_updateCPUcount(ProcessList* super) { #ifdef HAVE_SENSORS_SENSORS_H /* When started with offline CPUs, libsensors does not monitor those, * even when they become online. */ - if (super->existingCPUs != 0 && (active > super->activeCPUs || currExisting > super->existingCPUs)) + if (host->existingCPUs != 0 && (active > host->activeCPUs || currExisting > host->existingCPUs)) LibSensors_reload(); #endif - super->activeCPUs = active; + host->activeCPUs = active; assert(existing == currExisting); - super->existingCPUs = currExisting; + host->existingCPUs = currExisting; } -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) { +ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList) { LinuxProcessList* this = xCalloc(1, sizeof(LinuxProcessList)); ProcessList* pl = &(this->super); - ProcessList_init(pl, Class(LinuxProcess), usersTable, pidMatchList, userId); + ProcessList_init(pl, Class(LinuxProcess), host, pidMatchList); LinuxProcessList_initTtyDrivers(this); // Initialize page size @@ -594,7 +595,7 @@ static bool LinuxProcessList_readStatusFile(Process* process, openat_arg_t procF return true; } -static bool LinuxProcessList_updateUser(ProcessList* processList, Process* process, openat_arg_t procFd) { +static bool LinuxProcessList_updateUser(const Machine* host, Process* process, openat_arg_t procFd) { struct stat sstat; #ifdef HAVE_OPENAT int statok = fstat(procFd, &sstat); @@ -606,7 +607,7 @@ static bool LinuxProcessList_updateUser(ProcessList* processList, Process* proce if (process->st_uid != sstat.st_uid) { process->st_uid = sstat.st_uid; - process->user = UsersTable_getRef(processList->usersTable, sstat.st_uid); + process->user = UsersTable_getRef(host->usersTable, sstat.st_uid); } return true; @@ -1421,14 +1422,14 @@ static char* LinuxProcessList_updateTtyDevice(TtyDriver* ttyDrivers, unsigned lo return out; } -static bool isOlderThan(const ProcessList* pl, const Process* proc, unsigned int seconds) { - assert(pl->realtimeMs > 0); +static bool isOlderThan(const Machine* host, const Process* proc, unsigned int seconds) { + assert(host->realtimeMs > 0); /* Starttime might not yet be parsed */ if (proc->starttime_ctime <= 0) return false; - uint64_t realtime = pl->realtimeMs / 1000; + uint64_t realtime = host->realtimeMs / 1000; if (realtime < (uint64_t)proc->starttime_ctime) return false; @@ -1438,9 +1439,10 @@ static bool isOlderThan(const ProcessList* pl, const Process* proc, unsigned int static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_t parentFd, const char* dirname, const Process* parent, double period) { ProcessList* pl = (ProcessList*) this; - const struct dirent* entry; - const Settings* settings = pl->settings; + const Machine* host = pl->host; + const Settings* settings = host->settings; const ScreenSettings* ss = settings->ss; + const struct dirent* entry; #ifdef HAVE_OPENAT int dirFd = openat(parentFd, dirname, O_RDONLY | O_DIRECTORY | O_NOFOLLOW); @@ -1457,7 +1459,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ return false; } - const unsigned int activeCPUs = pl->activeCPUs; + const unsigned int activeCPUs = host->activeCPUs; const bool hideKernelThreads = settings->hideKernelThreads; const bool hideUserlandThreads = settings->hideUserlandThreads; const bool hideRunningInContainer = settings->hideRunningInContainer; @@ -1543,7 +1545,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ bool scanMainThread = !hideUserlandThreads && !Process_isKernelThread(proc) && !parent; if (ss->flags & PROCESS_FLAG_IO) - LinuxProcessList_readIoFile(lp, procFd, scanMainThread, pl->realtimeMs); + LinuxProcessList_readIoFile(lp, procFd, scanMainThread, host->realtimeMs); if (!LinuxProcessList_readStatmFile(lp, procFd)) goto errorReadingProcess; @@ -1552,15 +1554,15 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ bool prev = proc->usesDeletedLib; if (!proc->isKernelThread && !proc->isUserlandThread && - ((ss->flags & PROCESS_FLAG_LINUX_LRS_FIX) || (settings->highlightDeletedExe && !proc->procExeDeleted && isOlderThan(pl, proc, 10)))) { + ((ss->flags & PROCESS_FLAG_LINUX_LRS_FIX) || (settings->highlightDeletedExe && !proc->procExeDeleted && isOlderThan(host, proc, 10)))) { // Check if we really should recalculate the M_LRS value for this process - uint64_t passedTimeInMs = pl->realtimeMs - lp->last_mlrs_calctime; + uint64_t passedTimeInMs = host->realtimeMs - lp->last_mlrs_calctime; uint64_t recheck = ((uint64_t)rand()) % 2048; if (passedTimeInMs > recheck) { - lp->last_mlrs_calctime = pl->realtimeMs; + lp->last_mlrs_calctime = host->realtimeMs; LinuxProcessList_readMaps(lp, procFd, ss->flags & PROCESS_FLAG_LINUX_LRS_FIX, settings->highlightDeletedExe); } } else { @@ -1610,10 +1612,10 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ /* period might be 0 after system sleep */ float percent_cpu = (period < 1E-6) ? 0.0F : ((lp->utime + lp->stime - lasttimes) / period * 100.0); proc->percent_cpu = CLAMP(percent_cpu, 0.0F, activeCPUs * 100.0F); - proc->percent_mem = proc->m_resident / (double)(pl->totalMem) * 100.0; + proc->percent_mem = proc->m_resident / (double)(host->totalMem) * 100.0; Process_updateCPUFieldWidths(proc->percent_cpu); - if (! LinuxProcessList_updateUser(pl, proc, procFd)) + if (! LinuxProcessList_updateUser(host, proc, procFd)) goto errorReadingProcess; if (!LinuxProcessList_readStatusFile(proc, procFd)) @@ -1733,6 +1735,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ } static inline void LinuxProcessList_scanMemoryInfo(ProcessList* this) { + Machine* host = this->host; LinuxProcessList *lpl = (LinuxProcessList *)this; memory_t availableMem = 0; memory_t freeMem = 0; @@ -1809,16 +1812,16 @@ static inline void LinuxProcessList_scanMemoryInfo(ProcessList* this) { * - Shmem in part of Cached (see https://lore.kernel.org/patchwork/patch/648763/), * do not show twice by subtracting from Cached and do not subtract twice from used. */ - this->totalMem = totalMem; - this->cachedMem = cachedMem + sreclaimableMem - sharedMem; - this->sharedMem = sharedMem; + host->totalMem = totalMem; + host->cachedMem = cachedMem + sreclaimableMem - sharedMem; + host->sharedMem = sharedMem; const memory_t usedDiff = freeMem + cachedMem + sreclaimableMem + buffersMem; - this->usedMem = (totalMem >= usedDiff) ? totalMem - usedDiff : totalMem - freeMem; - this->buffersMem = buffersMem; - this->availableMem = availableMem != 0 ? MINIMUM(availableMem, totalMem) : freeMem; - this->totalSwap = swapTotalMem; - this->usedSwap = swapTotalMem - swapFreeMem - swapCacheMem; - this->cachedSwap = swapCacheMem; + host->usedMem = (totalMem >= usedDiff) ? totalMem - usedDiff : totalMem - freeMem; + host->buffersMem = buffersMem; + host->availableMem = availableMem != 0 ? MINIMUM(availableMem, totalMem) : freeMem; + host->totalSwap = swapTotalMem; + host->usedSwap = swapTotalMem - swapFreeMem - swapCacheMem; + host->cachedSwap = swapCacheMem; lpl->zswap.usedZswapComp = zswapCompMem; lpl->zswap.usedZswapOrig = zswapOrigMem; } @@ -1880,6 +1883,7 @@ static void LinuxProcessList_scanHugePages(LinuxProcessList* this) { } static inline void LinuxProcessList_scanZswapInfo(LinuxProcessList *this) { + const Machine* host = this->super.host; long max_pool_percent = 0; int r; char buf[256]; @@ -1893,7 +1897,7 @@ static inline void LinuxProcessList_scanZswapInfo(LinuxProcessList *this) { return; } - this->zswap.totalZswapPool = this->super.totalMem * max_pool_percent / 100; + this->zswap.totalZswapPool = host->totalMem * max_pool_percent / 100; /* the rest of the metrics are set in LinuxProcessList_scanMemoryInfo() */ } @@ -2027,7 +2031,8 @@ static inline double LinuxProcessList_scanCPUTime(ProcessList* super) { if (!file) CRT_fatalError("Cannot open " PROCSTATFILE); - unsigned int existingCPUs = super->existingCPUs; + Machine* host = super->host; + unsigned int existingCPUs = host->existingCPUs; unsigned int lastAdjCpuId = 0; for (unsigned int i = 0; i <= existingCPUs; i++) { @@ -2056,7 +2061,7 @@ static inline double LinuxProcessList_scanCPUTime(ProcessList* super) { adjCpuId = cpuid + 1; } - if (adjCpuId > super->existingCPUs) + if (adjCpuId > host->existingCPUs) break; for (unsigned int j = lastAdjCpuId + 1; j < adjCpuId; j++) { @@ -2104,7 +2109,7 @@ static inline double LinuxProcessList_scanCPUTime(ProcessList* super) { cpuData->totalTime = totaltime; } - double period = (double)this->cpuData[0].totalPeriod / super->activeCPUs; + double period = (double)this->cpuData[0].totalPeriod / host->activeCPUs; char buffer[PROC_LINE_LENGTH + 1]; while (fgets(buffer, sizeof(buffer), file)) { @@ -2120,7 +2125,8 @@ static inline double LinuxProcessList_scanCPUTime(ProcessList* super) { } static int scanCPUFrequencyFromSysCPUFreq(LinuxProcessList* this) { - unsigned int existingCPUs = this->super.existingCPUs; + const Machine* host = this->super.host; + unsigned int existingCPUs = host->existingCPUs; int numCPUsWithFrequency = 0; unsigned long totalFrequency = 0; @@ -2139,7 +2145,7 @@ static int scanCPUFrequencyFromSysCPUFreq(LinuxProcessList* this) { } for (unsigned int i = 0; i < existingCPUs; ++i) { - if (!ProcessList_isCPUonline(&this->super, i)) + if (!Machine_isCPUonline(host, i)) continue; char pathBuffer[64]; @@ -2187,7 +2193,7 @@ static void scanCPUFrequencyFromCPUinfo(LinuxProcessList* this) { if (file == NULL) return; - unsigned int existingCPUs = this->super.existingCPUs; + unsigned int existingCPUs = this->super.host->existingCPUs; int numCPUsWithFrequency = 0; double totalFrequency = 0; int cpuid = -1; @@ -2228,7 +2234,7 @@ static void scanCPUFrequencyFromCPUinfo(LinuxProcessList* this) { } static void LinuxProcessList_scanCPUFrequency(LinuxProcessList* this) { - unsigned int existingCPUs = this->super.existingCPUs; + unsigned int existingCPUs = this->super.host->existingCPUs; for (unsigned int i = 0; i <= existingCPUs; i++) { this->cpuData[i].frequency = NAN; @@ -2243,7 +2249,9 @@ static void LinuxProcessList_scanCPUFrequency(LinuxProcessList* this) { void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { LinuxProcessList* this = (LinuxProcessList*) super; - const Settings* settings = super->settings; + + const Machine* host = super->host; + const Settings* settings = host->settings; LinuxProcessList_scanMemoryInfo(super); LinuxProcessList_scanHugePages(this); @@ -2259,7 +2267,7 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { #ifdef HAVE_SENSORS_SENSORS_H if (settings->showCPUTemperature) - LibSensors_getCPUTemperatures(this->cpuData, this->super.existingCPUs, this->super.activeCPUs); + LibSensors_getCPUTemperatures(this->cpuData, host->existingCPUs, host->activeCPUs); #endif // in pause mode only gather global data for meters (CPU/memory/...) @@ -2288,9 +2296,19 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { LinuxProcessList_recurseProcTree(this, rootFd, PROCDIR, NULL, period); } -bool ProcessList_isCPUonline(const ProcessList* super, unsigned int id) { - assert(id < super->existingCPUs); +Machine* Machine_new(UsersTable* usersTable, uid_t userId) { + Machine* this = xCalloc(1, sizeof(Machine)); + Machine_init(this, usersTable, userId); + return this; +} + +void Machine_delete(Machine* host) { + free(host); +} + +bool Machine_isCPUonline(const Machine* host, unsigned int id) { + assert(id < host->existingCPUs); - const LinuxProcessList* this = (const LinuxProcessList*) super; + const LinuxProcessList* this = (const LinuxProcessList*) host->pl; return this->cpuData[id + 1].online; } diff --git a/linux/LinuxProcessList.h b/linux/LinuxProcessList.h index 9cfdf24aa..69bae5c07 100644 --- a/linux/LinuxProcessList.h +++ b/linux/LinuxProcessList.h @@ -117,12 +117,16 @@ typedef struct LinuxProcessList_ { #define PROC_LINE_LENGTH 4096 #endif -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId); +ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList); void ProcessList_delete(ProcessList* pl); void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate); -bool ProcessList_isCPUonline(const ProcessList* super, unsigned int id); +Machine* Machine_new(UsersTable* usersTable, uid_t userId); + +bool Machine_isCPUonline(const Machine* host, unsigned int id); + +void Machine_delete(Machine* host); #endif diff --git a/linux/Platform.c b/linux/Platform.c index 782f15461..68cefce9b 100644 --- a/linux/Platform.c +++ b/linux/Platform.c @@ -304,7 +304,8 @@ int Platform_getMaxPid(void) { } double Platform_setCPUValues(Meter* this, unsigned int cpu) { - const LinuxProcessList* pl = (const LinuxProcessList*) this->pl; + const Machine* host = this->host; + const LinuxProcessList* pl = (const LinuxProcessList*) host->pl; const CPUData* cpuData = &(pl->cpuData[cpu]); double total = (double) ( cpuData->totalPeriod == 0 ? 1 : cpuData->totalPeriod); double percent; @@ -317,7 +318,7 @@ double Platform_setCPUValues(Meter* this, unsigned int cpu) { v[CPU_METER_NICE] = cpuData->nicePeriod / total * 100.0; v[CPU_METER_NORMAL] = cpuData->userPeriod / total * 100.0; - if (this->pl->settings->detailedCPUTime) { + if (host->settings->detailedCPUTime) { v[CPU_METER_KERNEL] = cpuData->systemPeriod / total * 100.0; v[CPU_METER_IRQ] = cpuData->irqPeriod / total * 100.0; v[CPU_METER_SOFTIRQ] = cpuData->softIrqPeriod / total * 100.0; @@ -326,7 +327,7 @@ double Platform_setCPUValues(Meter* this, unsigned int cpu) { v[CPU_METER_IOWAIT] = cpuData->ioWaitPeriod / total * 100.0; this->curItems = 8; percent = v[CPU_METER_NICE] + v[CPU_METER_NORMAL] + v[CPU_METER_KERNEL] + v[CPU_METER_IRQ] + v[CPU_METER_SOFTIRQ]; - if (this->pl->settings->accountGuestInCPUMeter) { + if (host->settings->accountGuestInCPUMeter) { percent += v[CPU_METER_STEAL] + v[CPU_METER_GUEST]; } } else { @@ -352,16 +353,16 @@ double Platform_setCPUValues(Meter* this, unsigned int cpu) { } void Platform_setMemoryValues(Meter* this) { - const ProcessList* pl = this->pl; - const LinuxProcessList* lpl = (const LinuxProcessList*) pl; + const Machine* host = this->host; + const LinuxProcessList* lpl = (const LinuxProcessList*) host->pl; - this->total = pl->totalMem; - this->values[MEMORY_METER_USED] = pl->usedMem; - this->values[MEMORY_METER_BUFFERS] = pl->buffersMem; - this->values[MEMORY_METER_SHARED] = pl->sharedMem; + this->total = host->totalMem; + this->values[MEMORY_METER_USED] = host->usedMem; + this->values[MEMORY_METER_BUFFERS] = host->buffersMem; + this->values[MEMORY_METER_SHARED] = host->sharedMem; this->values[MEMORY_METER_COMPRESSED] = 0; /* compressed */ - this->values[MEMORY_METER_CACHE] = pl->cachedMem; - this->values[MEMORY_METER_AVAILABLE] = pl->availableMem; + this->values[MEMORY_METER_CACHE] = host->cachedMem; + this->values[MEMORY_METER_AVAILABLE] = host->availableMem; if (lpl->zfs.enabled != 0 && !Running_containerized) { // ZFS does not shrink below the value of zfs_arc_min. @@ -380,12 +381,12 @@ void Platform_setMemoryValues(Meter* this) { } void Platform_setSwapValues(Meter* this) { - const ProcessList* pl = this->pl; - const LinuxProcessList* lpl = (const LinuxProcessList*) pl; + const Machine* host = this->host; + const LinuxProcessList* lpl = (const LinuxProcessList*) host->pl; - this->total = pl->totalSwap; - this->values[SWAP_METER_USED] = pl->usedSwap; - this->values[SWAP_METER_CACHE] = pl->cachedSwap; + this->total = host->totalSwap; + this->values[SWAP_METER_USED] = host->usedSwap; + this->values[SWAP_METER_CACHE] = host->cachedSwap; this->values[SWAP_METER_FRONTSWAP] = 0; /* frontswap -- memory that is accounted to swap but resides elsewhere */ if (lpl->zswap.usedZswapOrig > 0 || lpl->zswap.usedZswapComp > 0) { @@ -410,20 +411,20 @@ void Platform_setSwapValues(Meter* this) { } void Platform_setZramValues(Meter* this) { - const LinuxProcessList* lpl = (const LinuxProcessList*) this->pl; + const LinuxProcessList* lpl = (const LinuxProcessList*) this->host->pl; this->total = lpl->zram.totalZram; this->values[ZRAM_METER_COMPRESSED] = lpl->zram.usedZramComp; this->values[ZRAM_METER_UNCOMPRESSED] = lpl->zram.usedZramOrig; } void Platform_setZfsArcValues(Meter* this) { - const LinuxProcessList* lpl = (const LinuxProcessList*) this->pl; + const LinuxProcessList* lpl = (const LinuxProcessList*) this->host->pl; ZfsArcMeter_readStats(this, &(lpl->zfs)); } void Platform_setZfsCompressedArcValues(Meter* this) { - const LinuxProcessList* lpl = (const LinuxProcessList*) this->pl; + const LinuxProcessList* lpl = (const LinuxProcessList*) this->host->pl; ZfsCompressedArcMeter_readStats(this, &(lpl->zfs)); } diff --git a/netbsd/NetBSDProcess.c b/netbsd/NetBSDProcess.c index 4d4ac4ee2..bdb0f50c0 100644 --- a/netbsd/NetBSDProcess.c +++ b/netbsd/NetBSDProcess.c @@ -212,10 +212,10 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = { }; -Process* NetBSDProcess_new(const Settings* settings) { +Process* NetBSDProcess_new(const Machine* host) { NetBSDProcess* this = xCalloc(1, sizeof(NetBSDProcess)); Object_setClass(this, Class(NetBSDProcess)); - Process_init(&this->super, settings); + Process_init(&this->super, host); return &this->super; } diff --git a/netbsd/NetBSDProcess.h b/netbsd/NetBSDProcess.h index b9e6b26a9..1c068a4ab 100644 --- a/netbsd/NetBSDProcess.h +++ b/netbsd/NetBSDProcess.h @@ -12,9 +12,9 @@ in the source distribution for its full text. #include +#include "Machine.h" #include "Object.h" #include "Process.h" -#include "Settings.h" typedef struct NetBSDProcess_ { @@ -25,7 +25,7 @@ extern const ProcessClass NetBSDProcess_class; extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD]; -Process* NetBSDProcess_new(const Settings* settings); +Process* NetBSDProcess_new(const Machine* host); void Process_delete(Object* cast); diff --git a/netbsd/NetBSDProcessList.c b/netbsd/NetBSDProcessList.c index 381672d65..b7b6915a7 100644 --- a/netbsd/NetBSDProcessList.c +++ b/netbsd/NetBSDProcessList.c @@ -54,6 +54,7 @@ static const struct { static void NetBSDProcessList_updateCPUcount(ProcessList* super) { NetBSDProcessList* opl = (NetBSDProcessList*) super; + Machine* host = super->host; // Definitions for sysctl(3), cf. https://nxr.netbsd.org/xref/src/sys/sys/sysctl.h#813 const int mib_ncpu_existing[] = { CTL_HW, HW_NCPU }; // Number of existing CPUs @@ -72,8 +73,8 @@ static void NetBSDProcessList_updateCPUcount(ProcessList* super) { value = 1; } - if (value != super->activeCPUs) { - super->activeCPUs = value; + if (value != host->activeCPUs) { + host->activeCPUs = value; change = true; } @@ -81,12 +82,12 @@ static void NetBSDProcessList_updateCPUcount(ProcessList* super) { size = sizeof(value); r = sysctl(mib_ncpu_existing, 2, &value, &size, NULL, 0); if (r < 0 || value < 1) { - value = super->activeCPUs; + value = host->activeCPUs; } - if (value != super->existingCPUs) { + if (value != host->existingCPUs) { opl->cpuData = xReallocArray(opl->cpuData, value + 1, sizeof(CPUData)); - super->existingCPUs = value; + host->existingCPUs = value; change = true; } @@ -97,7 +98,7 @@ static void NetBSDProcessList_updateCPUcount(ProcessList* super) { dAvg->totalTime = 1; dAvg->totalPeriod = 1; - for (unsigned int i = 0; i < super->existingCPUs; i++) { + for (unsigned int i = 0; i < host->existingCPUs; i++) { CPUData* d = &opl->cpuData[i + 1]; memset(d, '\0', sizeof(CPUData)); d->totalTime = 1; @@ -106,14 +107,14 @@ static void NetBSDProcessList_updateCPUcount(ProcessList* super) { } } -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) { +ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList) { const int fmib[] = { CTL_KERN, KERN_FSCALE }; size_t size; char errbuf[_POSIX2_LINE_MAX]; NetBSDProcessList* npl = xCalloc(1, sizeof(NetBSDProcessList)); ProcessList* pl = (ProcessList*) npl; - ProcessList_init(pl, Class(NetBSDProcess), usersTable, pidMatchList, userId); + ProcessList_init(pl, Class(NetBSDProcess), host, pidMatchList); NetBSDProcessList_updateCPUcount(pl); @@ -147,7 +148,7 @@ void ProcessList_delete(ProcessList* this) { free(this); } -static void NetBSDProcessList_scanMemoryInfo(ProcessList* pl) { +static void NetBSDProcessList_scanMemoryInfo(Machine* host) { static int uvmexp_mib[] = {CTL_VM, VM_UVMEXP2}; struct uvmexp_sysctl uvmexp; size_t size_uvmexp = sizeof(uvmexp); @@ -156,12 +157,12 @@ static void NetBSDProcessList_scanMemoryInfo(ProcessList* pl) { CRT_fatalError("uvmexp sysctl call failed"); } - pl->totalMem = uvmexp.npages * pageSizeKB; - pl->buffersMem = 0; - pl->cachedMem = (uvmexp.filepages + uvmexp.execpages) * pageSizeKB; - pl->usedMem = (uvmexp.active + uvmexp.wired) * pageSizeKB; - pl->totalSwap = uvmexp.swpages * pageSizeKB; - pl->usedSwap = uvmexp.swpginuse * pageSizeKB; + host->totalMem = uvmexp.npages * pageSizeKB; + host->buffersMem = 0; + host->cachedMem = (uvmexp.filepages + uvmexp.execpages) * pageSizeKB; + host->usedMem = (uvmexp.active + uvmexp.wired) * pageSizeKB; + host->totalSwap = uvmexp.swpages * pageSizeKB; + host->usedSwap = uvmexp.swpginuse * pageSizeKB; } static void NetBSDProcessList_updateExe(const struct kinfo_proc2* kproc, Process* proc) { @@ -262,7 +263,8 @@ static double getpcpu(const struct kinfo_proc2* kp) { } static void NetBSDProcessList_scanProcs(NetBSDProcessList* this) { - const Settings* settings = this->super.settings; + const Machine* host = this->super.host; + const Settings* settings = host->settings; bool hideKernelThreads = settings->hideKernelThreads; bool hideUserlandThreads = settings->hideUserlandThreads; int count = 0; @@ -313,14 +315,14 @@ static void NetBSDProcessList_scanProcs(NetBSDProcessList* this) { if (proc->st_uid != kproc->p_uid) { proc->st_uid = kproc->p_uid; - proc->user = UsersTable_getRef(this->super.usersTable, proc->st_uid); + proc->user = UsersTable_getRef(host->usersTable, proc->st_uid); } proc->m_virt = kproc->p_vm_vsize; proc->m_resident = kproc->p_vm_rssize; - proc->percent_mem = (proc->m_resident * pageSizeKB) / (double)(this->super.totalMem) * 100.0; - proc->percent_cpu = CLAMP(getpcpu(kproc), 0.0, this->super.activeCPUs * 100.0); + proc->percent_mem = (proc->m_resident * pageSizeKB) / (double)(host->totalMem) * 100.0; + proc->percent_cpu = CLAMP(getpcpu(kproc), 0.0, host->activeCPUs * 100.0); Process_updateCPUFieldWidths(proc->percent_cpu); proc->nlwp = kproc->p_nlwps; @@ -411,10 +413,11 @@ static void kernelCPUTimesToHtop(const u_int64_t* times, CPUData* cpu) { } static void NetBSDProcessList_scanCPUTime(NetBSDProcessList* this) { + const Machine* host = this->super.host; u_int64_t kernelTimes[CPUSTATES] = {0}; u_int64_t avg[CPUSTATES] = {0}; - for (unsigned int i = 0; i < this->super.existingCPUs; i++) { + for (unsigned int i = 0; i < host->existingCPUs; i++) { getKernelCPUTimes(i, kernelTimes); CPUData* cpu = &this->cpuData[i + 1]; kernelCPUTimesToHtop(kernelTimes, cpu); @@ -427,14 +430,15 @@ static void NetBSDProcessList_scanCPUTime(NetBSDProcessList* this) { } for (int i = 0; i < CPUSTATES; i++) { - avg[i] /= this->super.activeCPUs; + avg[i] /= host->activeCPUs; } kernelCPUTimesToHtop(avg, &this->cpuData[0]); } static void NetBSDProcessList_scanCPUFrequency(NetBSDProcessList* this) { - unsigned int cpus = this->super.existingCPUs; + const Machine* host = this->super.host; + unsigned int cpus = host->existingCPUs; bool match = false; char name[64]; long int freq = 0; @@ -481,7 +485,7 @@ static void NetBSDProcessList_scanCPUFrequency(NetBSDProcessList* this) { void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { NetBSDProcessList* npl = (NetBSDProcessList*) super; - NetBSDProcessList_scanMemoryInfo(super); + NetBSDProcessList_scanMemoryInfo(super->host); NetBSDProcessList_scanCPUTime(npl); if (super->settings->showCPUFrequency) { @@ -496,8 +500,18 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { NetBSDProcessList_scanProcs(npl); } -bool ProcessList_isCPUonline(const ProcessList* super, unsigned int id) { - assert(id < super->existingCPUs); +Machine* Machine_new(UsersTable* usersTable, uid_t userId) { + Machine* this = xCalloc(1, sizeof(Machine)); + Machine_init(this, usersTable, userId); + return this; +} + +void Machine_delete(Machine* host) { + free(host); +} + +bool Machine_isCPUonline(const Machine* host, unsigned int id) { + assert(id < host->existingCPUs); // TODO: Support detecting online / offline CPUs. return true; diff --git a/netbsd/NetBSDProcessList.h b/netbsd/NetBSDProcessList.h index d5a757fe5..0fe18b03f 100644 --- a/netbsd/NetBSDProcessList.h +++ b/netbsd/NetBSDProcessList.h @@ -49,10 +49,16 @@ typedef struct NetBSDProcessList_ { } NetBSDProcessList; -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId); +ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList); void ProcessList_delete(ProcessList* this); void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate); +Machine* Machine_new(UsersTable* usersTable, uid_t userId); + +bool Machine_isCPUonline(const Machine* host, unsigned int id); + +void Machine_delete(Machine* host); + #endif diff --git a/netbsd/Platform.c b/netbsd/Platform.c index 1cf160670..ea81a26a4 100644 --- a/netbsd/Platform.c +++ b/netbsd/Platform.c @@ -237,7 +237,8 @@ int Platform_getMaxPid(void) { } double Platform_setCPUValues(Meter* this, int cpu) { - const NetBSDProcessList* npl = (const NetBSDProcessList*) this->pl; + const Machine* host = this->host; + const NetBSDProcessList* npl = (const NetBSDProcessList*) host->pl; const CPUData* cpuData = &npl->cpuData[cpu]; double total = cpuData->totalPeriod == 0 ? 1 : cpuData->totalPeriod; double totalPercent; @@ -245,7 +246,7 @@ double Platform_setCPUValues(Meter* this, int cpu) { v[CPU_METER_NICE] = cpuData->nicePeriod / total * 100.0; v[CPU_METER_NORMAL] = cpuData->userPeriod / total * 100.0; - if (this->pl->settings->detailedCPUTime) { + if (host->settings->detailedCPUTime) { v[CPU_METER_KERNEL] = cpuData->sysPeriod / total * 100.0; v[CPU_METER_IRQ] = cpuData->intrPeriod / total * 100.0; v[CPU_METER_SOFTIRQ] = 0.0; @@ -269,20 +270,20 @@ double Platform_setCPUValues(Meter* this, int cpu) { } void Platform_setMemoryValues(Meter* this) { - const ProcessList* pl = this->pl; - this->total = pl->totalMem; - this->values[MEMORY_METER_USED] = pl->usedMem; - this->values[MEMORY_METER_BUFFERS] = pl->buffersMem; + const Machine* host = this->host; + this->total = host->totalMem; + this->values[MEMORY_METER_USED] = host->usedMem; + this->values[MEMORY_METER_BUFFERS] = host->buffersMem; // this->values[MEMORY_METER_SHARED] = "shared memory, like tmpfs and shm" // this->values[MEMORY_METER_COMPRESSED] = "compressed memory, like zswap on linux" - this->values[MEMORY_METER_CACHE] = pl->cachedMem; + this->values[MEMORY_METER_CACHE] = host->cachedMem; // this->values[MEMORY_METER_AVAILABLE] = "available memory" } void Platform_setSwapValues(Meter* this) { - const ProcessList* pl = this->pl; - this->total = pl->totalSwap; - this->values[SWAP_METER_USED] = pl->usedSwap; + const Machine* host = this->host; + this->total = host->totalSwap; + this->values[SWAP_METER_USED] = host->usedSwap; // this->values[SWAP_METER_CACHE] = "pages that are both in swap and RAM, like SwapCached on linux" // this->values[SWAP_METER_FRONTSWAP] = "pages that are accounted to swap but stored elsewhere, like frontswap on linux" } diff --git a/openbsd/OpenBSDProcess.c b/openbsd/OpenBSDProcess.c index c2f2ed4c8..0875dc00f 100644 --- a/openbsd/OpenBSDProcess.c +++ b/openbsd/OpenBSDProcess.c @@ -204,10 +204,10 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = { }; -Process* OpenBSDProcess_new(const Settings* settings) { +Process* OpenBSDProcess_new(const Machine* host) { OpenBSDProcess* this = xCalloc(1, sizeof(OpenBSDProcess)); Object_setClass(this, Class(OpenBSDProcess)); - Process_init(&this->super, settings); + Process_init(&this->super, host); return &this->super; } diff --git a/openbsd/OpenBSDProcess.h b/openbsd/OpenBSDProcess.h index 898c53774..aac4b3182 100644 --- a/openbsd/OpenBSDProcess.h +++ b/openbsd/OpenBSDProcess.h @@ -10,9 +10,9 @@ in the source distribution for its full text. #include +#include "Machine.h" #include "Object.h" #include "Process.h" -#include "Settings.h" typedef struct OpenBSDProcess_ { @@ -26,7 +26,7 @@ extern const ProcessClass OpenBSDProcess_class; extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD]; -Process* OpenBSDProcess_new(const Settings* settings); +Process* OpenBSDProcess_new(const Machine* host); void Process_delete(Object* cast); diff --git a/openbsd/OpenBSDProcessList.c b/openbsd/OpenBSDProcessList.c index 6ad3a7607..c83eb5430 100644 --- a/openbsd/OpenBSDProcessList.c +++ b/openbsd/OpenBSDProcessList.c @@ -38,6 +38,7 @@ static int pageSizeKB; static void OpenBSDProcessList_updateCPUcount(ProcessList* super) { OpenBSDProcessList* opl = (OpenBSDProcessList*) super; + Machine* host = super->host; const int nmib[] = { CTL_HW, HW_NCPU }; const int mib[] = { CTL_HW, HW_NCPUONLINE }; int r; @@ -51,20 +52,20 @@ static void OpenBSDProcessList_updateCPUcount(ProcessList* super) { value = 1; } - if (value != super->activeCPUs) { - super->activeCPUs = value; + if (value != host->activeCPUs) { + host->activeCPUs = value; change = true; } size = sizeof(value); r = sysctl(nmib, 2, &value, &size, NULL, 0); if (r < 0 || value < 1) { - value = super->activeCPUs; + value = host->activeCPUs; } - if (value != super->existingCPUs) { + if (value != host->existingCPUs) { opl->cpuData = xReallocArray(opl->cpuData, value + 1, sizeof(CPUData)); - super->existingCPUs = value; + host->existingCPUs = value; change = true; } @@ -75,7 +76,7 @@ static void OpenBSDProcessList_updateCPUcount(ProcessList* super) { dAvg->totalPeriod = 1; dAvg->online = true; - for (unsigned int i = 0; i < super->existingCPUs; i++) { + for (unsigned int i = 0; i < host->existingCPUs; i++) { CPUData* d = &opl->cpuData[i + 1]; memset(d, '\0', sizeof(CPUData)); d->totalTime = 1; @@ -94,14 +95,14 @@ static void OpenBSDProcessList_updateCPUcount(ProcessList* super) { } -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) { +ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList) { const int fmib[] = { CTL_KERN, KERN_FSCALE }; size_t size; char errbuf[_POSIX2_LINE_MAX]; OpenBSDProcessList* opl = xCalloc(1, sizeof(OpenBSDProcessList)); ProcessList* pl = (ProcessList*) opl; - ProcessList_init(pl, Class(OpenBSDProcess), usersTable, pidMatchList, userId); + ProcessList_init(pl, Class(OpenBSDProcess), host, pidMatchList); OpenBSDProcessList_updateCPUcount(pl); @@ -138,6 +139,7 @@ void ProcessList_delete(ProcessList* this) { } static void OpenBSDProcessList_scanMemoryInfo(ProcessList* pl) { + Machine* host = pl->host; const int uvmexp_mib[] = { CTL_VM, VM_UVMEXP }; struct uvmexp uvmexp; size_t size_uvmexp = sizeof(uvmexp); @@ -146,8 +148,8 @@ static void OpenBSDProcessList_scanMemoryInfo(ProcessList* pl) { CRT_fatalError("uvmexp sysctl call failed"); } - pl->totalMem = uvmexp.npages * pageSizeKB; - pl->usedMem = (uvmexp.npages - uvmexp.free - uvmexp.paging) * pageSizeKB; + host->totalMem = uvmexp.npages * pageSizeKB; + host->usedMem = (uvmexp.npages - uvmexp.free - uvmexp.paging) * pageSizeKB; // Taken from OpenBSD systat/iostat.c, top/machine.c and uvm_sysctl(9) const int bcache_mib[] = { CTL_VFS, VFS_GENERIC, VFS_BCACHESTAT }; @@ -158,7 +160,7 @@ static void OpenBSDProcessList_scanMemoryInfo(ProcessList* pl) { CRT_fatalError("cannot get vfs.bcachestat"); } - pl->cachedMem = bcstats.numbufpages * pageSizeKB; + host->cachedMem = bcstats.numbufpages * pageSizeKB; /* * Copyright (c) 1994 Thorsten Lockert @@ -183,10 +185,10 @@ static void OpenBSDProcessList_scanMemoryInfo(ProcessList* pl) { } } - pl->totalSwap = total; - pl->usedSwap = used; + host->totalSwap = total; + host->usedSwap = used; } else { - pl->totalSwap = pl->usedSwap = 0; + host->totalSwap = host->usedSwap = 0; } } @@ -270,7 +272,8 @@ static double getpcpu(const struct kinfo_proc* kp) { } static void OpenBSDProcessList_scanProcs(OpenBSDProcessList* this) { - const Settings* settings = this->super.settings; + Machine* host = this->super.host; + const Settings* settings = host->settings; const bool hideKernelThreads = settings->hideKernelThreads; const bool hideUserlandThreads = settings->hideUserlandThreads; int count = 0; @@ -331,8 +334,8 @@ static void OpenBSDProcessList_scanProcs(OpenBSDProcessList* this) { proc->m_virt = kproc->p_vm_dsize * pageSizeKB; proc->m_resident = kproc->p_vm_rssize * pageSizeKB; - proc->percent_mem = proc->m_resident / (float)this->super.totalMem * 100.0F; - proc->percent_cpu = CLAMP(getpcpu(kproc), 0.0F, this->super.activeCPUs * 100.0F); + proc->percent_mem = proc->m_resident / (float)host->totalMem * 100.0F; + proc->percent_cpu = CLAMP(getpcpu(kproc), 0.0F, host->activeCPUs * 100.0F); Process_updateCPUFieldWidths(proc->percent_cpu); proc->nice = kproc->p_nice - 20; @@ -345,7 +348,7 @@ static void OpenBSDProcessList_scanProcs(OpenBSDProcessList* this) { if (proc->st_uid != kproc->p_uid) { proc->st_uid = kproc->p_uid; - proc->user = UsersTable_getRef(this->super.usersTable, proc->st_uid); + proc->user = UsersTable_getRef(host->usersTable, proc->st_uid); } /* Taken from: https://github.com/openbsd/src/blob/6a38af0976a6870911f4b2de19d8da28723a5778/sys/sys/proc.h#L420 */ @@ -422,10 +425,11 @@ static void kernelCPUTimesToHtop(const u_int64_t* times, CPUData* cpu) { } static void OpenBSDProcessList_scanCPUTime(OpenBSDProcessList* this) { + Machine* host = this->super.host; u_int64_t kernelTimes[CPUSTATES] = {0}; u_int64_t avg[CPUSTATES] = {0}; - for (unsigned int i = 0; i < this->super.existingCPUs; i++) { + for (unsigned int i = 0; i < host->existingCPUs; i++) { CPUData* cpu = &this->cpuData[i + 1]; if (!cpu->online) { @@ -446,7 +450,7 @@ static void OpenBSDProcessList_scanCPUTime(OpenBSDProcessList* this) { } for (int i = 0; i < CPUSTATES; i++) { - avg[i] /= this->super.activeCPUs; + avg[i] /= host->activeCPUs; } kernelCPUTimesToHtop(avg, &this->cpuData[0]); @@ -478,9 +482,19 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { OpenBSDProcessList_scanProcs(opl); } -bool ProcessList_isCPUonline(const ProcessList* super, unsigned int id) { - assert(id < super->existingCPUs); +Machine* Machine_new(UsersTable* usersTable, uid_t userId) { + Machine* this = xCalloc(1, sizeof(Machine)); + Machine_init(this, usersTable, userId); + return this; +} + +void Machine_delete(Machine* host) { + free(host); +} + +bool Machine_isCPUonline(const Machine* host, unsigned int id) { + assert(id < host->existingCPUs); - const OpenBSDProcessList* opl = (const OpenBSDProcessList*) super; + const OpenBSDProcessList* opl = (const OpenBSDProcessList*) host->pl; return opl->cpuData[id + 1].online; } diff --git a/openbsd/OpenBSDProcessList.h b/openbsd/OpenBSDProcessList.h index 99de96cd1..6ca2ba2c1 100644 --- a/openbsd/OpenBSDProcessList.h +++ b/openbsd/OpenBSDProcessList.h @@ -49,12 +49,16 @@ typedef struct OpenBSDProcessList_ { } OpenBSDProcessList; -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId); +ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList); void ProcessList_delete(ProcessList* this); void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate); -bool ProcessList_isCPUonline(const ProcessList* super, unsigned int id); +Machine* Machine_new(UsersTable* usersTable, uid_t userId); + +bool Machine_isCPUonline(const Machine* host, unsigned int id); + +void Machine_delete(Machine* host); #endif diff --git a/openbsd/Platform.c b/openbsd/Platform.c index 707d21068..03c368566 100644 --- a/openbsd/Platform.c +++ b/openbsd/Platform.c @@ -181,7 +181,8 @@ int Platform_getMaxPid(void) { } double Platform_setCPUValues(Meter* this, unsigned int cpu) { - const OpenBSDProcessList* pl = (const OpenBSDProcessList*) this->pl; + const Machine* host = this->host; + const OpenBSDProcessList* pl = (const OpenBSDProcessList*) host->pl; const CPUData* cpuData = &(pl->cpuData[cpu]); double total; double totalPercent; @@ -196,7 +197,7 @@ double Platform_setCPUValues(Meter* this, unsigned int cpu) { v[CPU_METER_NICE] = cpuData->nicePeriod / total * 100.0; v[CPU_METER_NORMAL] = cpuData->userPeriod / total * 100.0; - if (this->pl->settings->detailedCPUTime) { + if (host->settings->detailedCPUTime) { v[CPU_METER_KERNEL] = cpuData->sysPeriod / total * 100.0; v[CPU_METER_IRQ] = cpuData->intrPeriod / total * 100.0; v[CPU_METER_SOFTIRQ] = 0.0; @@ -222,12 +223,12 @@ double Platform_setCPUValues(Meter* this, unsigned int cpu) { } void Platform_setMemoryValues(Meter* this) { - const ProcessList* pl = this->pl; - long int usedMem = pl->usedMem; - long int buffersMem = pl->buffersMem; - long int cachedMem = pl->cachedMem; + const Machine* host = this->host; + long int usedMem = host->usedMem; + long int buffersMem = host->buffersMem; + long int cachedMem = host->cachedMem; usedMem -= buffersMem + cachedMem; - this->total = pl->totalMem; + this->total = host->totalMem; this->values[MEMORY_METER_USED] = usedMem; this->values[MEMORY_METER_BUFFERS] = buffersMem; // this->values[MEMORY_METER_SHARED] = "shared memory, like tmpfs and shm" @@ -237,9 +238,9 @@ void Platform_setMemoryValues(Meter* this) { } void Platform_setSwapValues(Meter* this) { - const ProcessList* pl = this->pl; - this->total = pl->totalSwap; - this->values[SWAP_METER_USED] = pl->usedSwap; + const Machine* host = this->host; + this->total = host->totalSwap; + this->values[SWAP_METER_USED] = host->usedSwap; // this->values[SWAP_METER_CACHE] = "pages that are both in swap and RAM, like SwapCached on linux" // this->values[SWAP_METER_FRONTSWAP] = "pages that are accounted to swap but stored elsewhere, like frontswap on linux" } diff --git a/pcp/PCPDynamicColumn.c b/pcp/PCPDynamicColumn.c index 8c35fc10e..23b896e6f 100644 --- a/pcp/PCPDynamicColumn.c +++ b/pcp/PCPDynamicColumn.c @@ -304,7 +304,7 @@ void PCPDynamicColumn_writeField(PCPDynamicColumn* this, const Process* proc, Ri } int PCPDynamicColumn_compareByKey(const PCPProcess* p1, const PCPProcess* p2, ProcessField key) { - const PCPDynamicColumn* column = Hashtable_get(p1->super.settings->dynamicColumns, key); + const PCPDynamicColumn* column = Hashtable_get(p1->super.host->settings->dynamicColumns, key); if (!column) return -1; diff --git a/pcp/PCPProcess.c b/pcp/PCPProcess.c index b8b87cab5..cefd0ac3a 100644 --- a/pcp/PCPProcess.c +++ b/pcp/PCPProcess.c @@ -88,10 +88,10 @@ const ProcessFieldData Process_fields[] = { [AUTOGROUP_NICE] = { .name = "AUTOGROUP_NICE", .title = " ANI", .description = "Nice value (the higher the value, the more other processes take priority) associated with the process autogroup", .flags = PROCESS_FLAG_LINUX_AUTOGROUP, }, }; -Process* PCPProcess_new(const Settings* settings) { +Process* PCPProcess_new(const Machine* host) { PCPProcess* this = xCalloc(1, sizeof(PCPProcess)); Object_setClass(this, Class(PCPProcess)); - Process_init(&this->super, settings); + Process_init(&this->super, host); return &this->super; } @@ -113,7 +113,7 @@ static void PCPProcess_printDelay(float delay_percent, char* buffer, int n) { static void PCPProcess_writeField(const Process* this, RichString* str, ProcessField field) { const PCPProcess* pp = (const PCPProcess*) this; - bool coloring = this->settings->highlightMegabytes; + bool coloring = this->host->settings->highlightMegabytes; char buffer[256]; buffer[255] = '\0'; int attr = CRT_colors[DEFAULT_COLOR]; int n = sizeof(buffer) - 1; diff --git a/pcp/PCPProcess.h b/pcp/PCPProcess.h index 46ba07fee..be33111e0 100644 --- a/pcp/PCPProcess.h +++ b/pcp/PCPProcess.h @@ -13,9 +13,9 @@ in the source distribution for its full text. #include +#include "Machine.h" #include "Object.h" #include "Process.h" -#include "Settings.h" #define PROCESS_FLAG_LINUX_CGROUP 0x00000800 @@ -93,7 +93,7 @@ extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD]; extern const ProcessClass PCPProcess_class; -Process* PCPProcess_new(const Settings* settings); +Process* PCPProcess_new(const Machine* host); void Process_delete(Object* cast); diff --git a/pcp/PCPProcessList.c b/pcp/PCPProcessList.c index a5eb64d66..0e345fc05 100644 --- a/pcp/PCPProcessList.c +++ b/pcp/PCPProcessList.c @@ -19,6 +19,7 @@ in the source distribution for its full text. #include #include "Macros.h" +#include "Machine.h" #include "Object.h" #include "Platform.h" #include "Process.h" @@ -30,16 +31,16 @@ in the source distribution for its full text. static void PCPProcessList_updateCPUcount(PCPProcessList* this) { - ProcessList* pl = &(this->super); - pl->activeCPUs = PCPMetric_instanceCount(PCP_PERCPU_SYSTEM); + Machine* host = this->super.host; + host->activeCPUs = PCPMetric_instanceCount(PCP_PERCPU_SYSTEM); unsigned int cpus = Platform_getMaxCPU(); - if (cpus == pl->existingCPUs) + if (cpus == host->existingCPUs) return; if (cpus == 0) - cpus = pl->activeCPUs; + cpus = host->activeCPUs; if (cpus <= 1) - cpus = pl->activeCPUs = 1; - pl->existingCPUs = cpus; + cpus = host->activeCPUs = 1; + host->existingCPUs = cpus; free(this->percpu); free(this->values); @@ -63,11 +64,11 @@ static char* setUser(UsersTable* this, unsigned int uid, int pid, int offset) { return name; } -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) { +ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList) { PCPProcessList* this = xCalloc(1, sizeof(PCPProcessList)); ProcessList* super = &(this->super); - ProcessList_init(super, Class(PCPProcess), usersTable, pidMatchList, userId); + ProcessList_init(super, Class(PCPProcess), host, pidMatchList); struct timeval timestamp; gettimeofday(×tamp, NULL); @@ -83,7 +84,7 @@ void ProcessList_delete(ProcessList* pl) { PCPProcessList* this = (PCPProcessList*) pl; ProcessList_done(pl); free(this->values); - for (unsigned int i = 0; i < pl->existingCPUs; i++) + for (unsigned int i = 0; i < pl->host->existingCPUs; i++) free(this->percpu[i]); free(this->percpu); free(this->cpu); @@ -341,7 +342,9 @@ static void PCPProcessList_updateCmdline(Process* process, int pid, int offset, static bool PCPProcessList_updateProcesses(PCPProcessList* this, double period, struct timeval* tv) { ProcessList* pl = (ProcessList*) this; - const Settings* settings = pl->settings; + + Machine* host = pl->host; + const Settings* settings = host->settings; bool hideKernelThreads = settings->hideKernelThreads; bool hideUserlandThreads = settings->hideUserlandThreads; @@ -406,11 +409,11 @@ static bool PCPProcessList_updateProcesses(PCPProcessList* this, double period, float percent_cpu = (pp->utime + pp->stime - lasttimes) / period * 100.0; proc->percent_cpu = isnan(percent_cpu) ? - 0.0 : CLAMP(percent_cpu, 0.0, pl->activeCPUs * 100.0); - proc->percent_mem = proc->m_resident / (double)pl->totalMem * 100.0; + 0.0 : CLAMP(percent_cpu, 0.0, host->activeCPUs * 100.0); + proc->percent_mem = proc->m_resident / (double)host->totalMem * 100.0; Process_updateCPUFieldWidths(proc->percent_cpu); - PCPProcessList_updateUsername(proc, pid, offset, pl->usersTable); + PCPProcessList_updateUsername(proc, pid, offset, host->usersTable); if (!preExisting) { PCPProcessList_updateCmdline(proc, pid, offset, command); @@ -465,39 +468,40 @@ static bool PCPProcessList_updateProcesses(PCPProcessList* this, double period, } static void PCPProcessList_updateMemoryInfo(ProcessList* super) { + Machine* host = super->host; unsigned long long int freeMem = 0; unsigned long long int swapFreeMem = 0; unsigned long long int sreclaimableMem = 0; - super->totalMem = super->usedMem = super->cachedMem = 0; - super->usedSwap = super->totalSwap = super->sharedMem = 0; + host->totalMem = host->usedMem = host->cachedMem = 0; + host->usedSwap = host->totalSwap = host->sharedMem = 0; pmAtomValue value; if (PCPMetric_values(PCP_MEM_TOTAL, &value, 1, PM_TYPE_U64) != NULL) - super->totalMem = value.ull; + host->totalMem = value.ull; if (PCPMetric_values(PCP_MEM_FREE, &value, 1, PM_TYPE_U64) != NULL) freeMem = value.ull; if (PCPMetric_values(PCP_MEM_BUFFERS, &value, 1, PM_TYPE_U64) != NULL) - super->buffersMem = value.ull; + host->buffersMem = value.ull; if (PCPMetric_values(PCP_MEM_SRECLAIM, &value, 1, PM_TYPE_U64) != NULL) sreclaimableMem = value.ull; if (PCPMetric_values(PCP_MEM_SHARED, &value, 1, PM_TYPE_U64) != NULL) - super->sharedMem = value.ull; + host->sharedMem = value.ull; if (PCPMetric_values(PCP_MEM_CACHED, &value, 1, PM_TYPE_U64) != NULL) - super->cachedMem = value.ull + sreclaimableMem - super->sharedMem; - const memory_t usedDiff = freeMem + super->cachedMem + sreclaimableMem + super->buffersMem; - super->usedMem = (super->totalMem >= usedDiff) ? - super->totalMem - usedDiff : super->totalMem - freeMem; + host->cachedMem = value.ull + sreclaimableMem - host->sharedMem; + const memory_t usedDiff = freeMem + host->cachedMem + sreclaimableMem + host->buffersMem; + host->usedMem = (host->totalMem >= usedDiff) ? + host->totalMem - usedDiff : host->totalMem - freeMem; if (PCPMetric_values(PCP_MEM_AVAILABLE, &value, 1, PM_TYPE_U64) != NULL) - super->availableMem = MINIMUM(value.ull, super->totalMem); + host->availableMem = MINIMUM(value.ull, host->totalMem); else - super->availableMem = freeMem; + host->availableMem = freeMem; if (PCPMetric_values(PCP_MEM_SWAPFREE, &value, 1, PM_TYPE_U64) != NULL) swapFreeMem = value.ull; if (PCPMetric_values(PCP_MEM_SWAPTOTAL, &value, 1, PM_TYPE_U64) != NULL) - super->totalSwap = value.ull; + host->totalSwap = value.ull; if (PCPMetric_values(PCP_MEM_SWAPCACHED, &value, 1, PM_TYPE_U64) != NULL) - super->cachedSwap = value.ull; - super->usedSwap = super->totalSwap - swapFreeMem - super->cachedSwap; + host->cachedSwap = value.ull; + host->usedSwap = host->totalSwap - swapFreeMem - host->cachedSwap; } /* make copies of previously sampled values to avoid overwrite */ @@ -576,7 +580,7 @@ static void PCPProcessList_updateAllCPUTime(PCPProcessList* this, PCPMetric metr static void PCPProcessList_updatePerCPUTime(PCPProcessList* this, PCPMetric metric, CPUMetric cpumetric) { - int cpus = this->super.existingCPUs; + int cpus = this->super.host->existingCPUs; if (PCPMetric_values(metric, this->values, cpus, PM_TYPE_U64) == NULL) memset(this->values, 0, cpus * sizeof(pmAtomValue)); for (int i = 0; i < cpus; i++) @@ -585,7 +589,7 @@ static void PCPProcessList_updatePerCPUTime(PCPProcessList* this, PCPMetric metr static void PCPProcessList_updatePerCPUReal(PCPProcessList* this, PCPMetric metric, CPUMetric cpumetric) { - int cpus = this->super.existingCPUs; + int cpus = this->super.host->existingCPUs; if (PCPMetric_values(metric, this->values, cpus, PM_TYPE_DOUBLE) == NULL) memset(this->values, 0, cpus * sizeof(pmAtomValue)); for (int i = 0; i < cpus; i++) @@ -630,6 +634,7 @@ static inline void PCPProcessList_scanZfsArcstats(PCPProcessList* this) { } static void PCPProcessList_updateHeader(ProcessList* super, const Settings* settings) { + Machine* host = super->host; PCPProcessList_updateMemoryInfo(super); PCPProcessList* this = (PCPProcessList*) super; @@ -647,7 +652,7 @@ static void PCPProcessList_updateHeader(ProcessList* super, const Settings* sett PCPProcessList_updateAllCPUTime(this, PCP_CPU_GUEST, CPU_GUEST_TIME); PCPProcessList_deriveCPUTime(this->cpu); - for (unsigned int i = 0; i < super->existingCPUs; i++) + for (unsigned int i = 0; i < host->existingCPUs; i++) PCPProcessList_backupCPUTime(this->percpu[i]); PCPProcessList_updatePerCPUTime(this, PCP_PERCPU_USER, CPU_USER_TIME); PCPProcessList_updatePerCPUTime(this, PCP_PERCPU_NICE, CPU_NICE_TIME); @@ -658,7 +663,7 @@ static void PCPProcessList_updateHeader(ProcessList* super, const Settings* sett PCPProcessList_updatePerCPUTime(this, PCP_PERCPU_SOFTIRQ, CPU_SOFTIRQ_TIME); PCPProcessList_updatePerCPUTime(this, PCP_PERCPU_STEAL, CPU_STEAL_TIME); PCPProcessList_updatePerCPUTime(this, PCP_PERCPU_GUEST, CPU_GUEST_TIME); - for (unsigned int i = 0; i < super->existingCPUs; i++) + for (unsigned int i = 0; i < host->existingCPUs; i++) PCPProcessList_deriveCPUTime(this->percpu[i]); if (settings->showCPUFrequency) @@ -669,7 +674,8 @@ static void PCPProcessList_updateHeader(ProcessList* super, const Settings* sett void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { PCPProcessList* this = (PCPProcessList*) super; - const Settings* settings = super->settings; + Machine* host = super->host; + const Settings* settings = host->settings; bool enabled = !pauseProcessUpdate; bool flagged = settings->showCPUFrequency; @@ -716,9 +722,19 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { PCPProcessList_updateProcesses(this, period, ×tamp); } -bool ProcessList_isCPUonline(const ProcessList* super, unsigned int id) { - assert(id < super->existingCPUs); - (void) super; +Machine* Machine_new(UsersTable* usersTable, uid_t userId) { + Machine* this = xCalloc(1, sizeof(Machine)); + Machine_init(this, usersTable, userId); + return this; +} + +void Machine_delete(Machine* host) { + free(host); +} + +bool Machine_isCPUonline(const Machine* host, unsigned int id) { + assert(id < host->existingCPUs); + (void) host; pmAtomValue value; if (PCPMetric_instance(PCP_PERCPU_SYSTEM, id, id, &value, PM_TYPE_U32)) diff --git a/pcp/PCPProcessList.h b/pcp/PCPProcessList.h index 90e9939c1..9bce9cd73 100644 --- a/pcp/PCPProcessList.h +++ b/pcp/PCPProcessList.h @@ -63,12 +63,16 @@ typedef struct PCPProcessList_ { ZfsArcStats zfs; } PCPProcessList; -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId); +ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList); void ProcessList_delete(ProcessList* pl); void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate); -bool ProcessList_isCPUonline(const ProcessList* super, unsigned int id); +Machine* Machine_new(UsersTable* usersTable, uid_t userId); + +bool Machine_isCPUonline(const Machine* host, unsigned int id); + +void Machine_delete(Machine* host); #endif diff --git a/pcp/Platform.c b/pcp/Platform.c index 87de2c2f7..181898aaa 100644 --- a/pcp/Platform.c +++ b/pcp/Platform.c @@ -485,6 +485,7 @@ long long Platform_getBootTime(void) { } static double Platform_setOneCPUValues(Meter* this, pmAtomValue* values) { + Settings* settings = this->host->settings; unsigned long long value = values[CPU_TOTAL_PERIOD].ull; double total = (double) (value == 0 ? 1 : value); @@ -493,7 +494,7 @@ static double Platform_setOneCPUValues(Meter* this, pmAtomValue* values) { double* v = this->values; v[CPU_METER_NICE] = values[CPU_NICE_PERIOD].ull / total * 100.0; v[CPU_METER_NORMAL] = values[CPU_USER_PERIOD].ull / total * 100.0; - if (this->pl->settings->detailedCPUTime) { + if (settings->detailedCPUTime) { v[CPU_METER_KERNEL] = values[CPU_SYSTEM_PERIOD].ull / total * 100.0; v[CPU_METER_IRQ] = values[CPU_IRQ_PERIOD].ull / total * 100.0; v[CPU_METER_SOFTIRQ] = values[CPU_SOFTIRQ_PERIOD].ull / total * 100.0; @@ -502,7 +503,7 @@ static double Platform_setOneCPUValues(Meter* this, pmAtomValue* values) { v[CPU_METER_IOWAIT] = values[CPU_IOWAIT_PERIOD].ull / total * 100.0; this->curItems = 8; percent = v[CPU_METER_NICE] + v[CPU_METER_NORMAL] + v[CPU_METER_KERNEL] + v[CPU_METER_IRQ] + v[CPU_METER_SOFTIRQ]; - if (this->pl->settings->accountGuestInCPUMeter) { + if (settings->accountGuestInCPUMeter) { percent += v[CPU_METER_STEAL] + v[CPU_METER_GUEST]; } } else { @@ -523,23 +524,24 @@ static double Platform_setOneCPUValues(Meter* this, pmAtomValue* values) { } double Platform_setCPUValues(Meter* this, int cpu) { - const PCPProcessList* pl = (const PCPProcessList*) this->pl; + const PCPProcessList* pl = (const PCPProcessList*) this->host->pl; if (cpu <= 0) /* use aggregate values */ return Platform_setOneCPUValues(this, pl->cpu); return Platform_setOneCPUValues(this, pl->percpu[cpu - 1]); } void Platform_setMemoryValues(Meter* this) { - const ProcessList* pl = this->pl; + const Machine* host = this->host; + const ProcessList* pl = host->pl; const PCPProcessList* ppl = (const PCPProcessList*) pl; - this->total = pl->totalMem; - this->values[MEMORY_METER_USED] = pl->usedMem; - this->values[MEMORY_METER_BUFFERS] = pl->buffersMem; - this->values[MEMORY_METER_SHARED] = pl->sharedMem; + this->total = host->totalMem; + this->values[MEMORY_METER_USED] = host->usedMem; + this->values[MEMORY_METER_BUFFERS] = host->buffersMem; + this->values[MEMORY_METER_SHARED] = host->sharedMem; // this->values[MEMORY_METER_COMPRESSED] = "compressed memory, like zswap on linux" - this->values[MEMORY_METER_CACHE] = pl->cachedMem; - this->values[MEMORY_METER_AVAILABLE] = pl->availableMem; + this->values[MEMORY_METER_CACHE] = host->cachedMem; + this->values[MEMORY_METER_AVAILABLE] = host->availableMem; if (ppl->zfs.enabled != 0) { // ZFS does not shrink below the value of zfs_arc_min. @@ -553,10 +555,10 @@ void Platform_setMemoryValues(Meter* this) { } void Platform_setSwapValues(Meter* this) { - const ProcessList* pl = this->pl; - this->total = pl->totalSwap; - this->values[SWAP_METER_USED] = pl->usedSwap; - this->values[SWAP_METER_CACHE] = pl->cachedSwap; + const Machine* host = this->host; + this->total = host->totalSwap; + this->values[SWAP_METER_USED] = host->usedSwap; + this->values[SWAP_METER_CACHE] = host->cachedSwap; // this->values[SWAP_METER_FRONTSWAP] = "pages that are accounted to swap but stored elsewhere, like frontswap on linux" } @@ -593,13 +595,13 @@ void Platform_setZramValues(Meter* this) { } void Platform_setZfsArcValues(Meter* this) { - const PCPProcessList* ppl = (const PCPProcessList*) this->pl; + const PCPProcessList* ppl = (const PCPProcessList*) this->host->pl; ZfsArcMeter_readStats(this, &(ppl->zfs)); } void Platform_setZfsCompressedArcValues(Meter* this) { - const PCPProcessList* ppl = (const PCPProcessList*) this->pl; + const PCPProcessList* ppl = (const PCPProcessList*) this->host->pl; ZfsCompressedArcMeter_readStats(this, &(ppl->zfs)); } diff --git a/solaris/Platform.c b/solaris/Platform.c index 79b6a9ead..8c88fc850 100644 --- a/solaris/Platform.c +++ b/solaris/Platform.c @@ -194,8 +194,9 @@ int Platform_getMaxPid(void) { } double Platform_setCPUValues(Meter* this, unsigned int cpu) { - const SolarisProcessList* spl = (const SolarisProcessList*) this->pl; - unsigned int cpus = this->pl->existingCPUs; + const Machine* host = this->host; + const SolarisProcessList* spl = (const SolarisProcessList*) host->pl; + unsigned int cpus = host->existingCPUs; const CPUData* cpuData = NULL; if (cpus == 1) { @@ -215,7 +216,7 @@ double Platform_setCPUValues(Meter* this, unsigned int cpu) { v[CPU_METER_NICE] = cpuData->nicePercent; v[CPU_METER_NORMAL] = cpuData->userPercent; - if (this->pl->settings->detailedCPUTime) { + if (host->settings->detailedCPUTime) { v[CPU_METER_KERNEL] = cpuData->systemPercent; v[CPU_METER_IRQ] = cpuData->irqPercent; this->curItems = 4; @@ -235,32 +236,32 @@ double Platform_setCPUValues(Meter* this, unsigned int cpu) { } void Platform_setMemoryValues(Meter* this) { - const ProcessList* pl = this->pl; - this->total = pl->totalMem; - this->values[MEMORY_METER_USED] = pl->usedMem; - this->values[MEMORY_METER_BUFFERS] = pl->buffersMem; + const Machine* host = this->host; + this->total = host->totalMem; + this->values[MEMORY_METER_USED] = host->usedMem; + this->values[MEMORY_METER_BUFFERS] = host->buffersMem; // this->values[MEMORY_METER_SHARED] = "shared memory, like tmpfs and shm" // this->values[MEMORY_METER_COMPRESSED] = "compressed memory, like zswap on linux" - this->values[MEMORY_METER_CACHE] = pl->cachedMem; + this->values[MEMORY_METER_CACHE] = host->cachedMem; // this->values[MEMORY_METER_AVAILABLE] = "available memory" } void Platform_setSwapValues(Meter* this) { - const ProcessList* pl = this->pl; - this->total = pl->totalSwap; - this->values[SWAP_METER_USED] = pl->usedSwap; + const Machine* host = this->host; + this->total = host->totalSwap; + this->values[SWAP_METER_USED] = host->usedSwap; // this->values[SWAP_METER_CACHE] = "pages that are both in swap and RAM, like SwapCached on linux" // this->values[SWAP_METER_FRONTSWAP] = "pages that are accounted to swap but stored elsewhere, like frontswap on linux" } void Platform_setZfsArcValues(Meter* this) { - const SolarisProcessList* spl = (const SolarisProcessList*) this->pl; + const SolarisProcessList* spl = (const SolarisProcessList*) this->host->pl; ZfsArcMeter_readStats(this, &(spl->zfs)); } void Platform_setZfsCompressedArcValues(Meter* this) { - const SolarisProcessList* spl = (const SolarisProcessList*) this->pl; + const SolarisProcessList* spl = (const SolarisProcessList*) this->host->pl; ZfsCompressedArcMeter_readStats(this, &(spl->zfs)); } diff --git a/solaris/SolarisProcess.c b/solaris/SolarisProcess.c index 840757e7d..3b5ea1ae5 100644 --- a/solaris/SolarisProcess.c +++ b/solaris/SolarisProcess.c @@ -59,10 +59,10 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = { [LWPID] = { .name = "LWPID", .title = "LWPID", .description = "LWP ID", .flags = 0, .pidColumn = true, }, }; -Process* SolarisProcess_new(const Settings* settings) { +Process* SolarisProcess_new(const Machine* host) { SolarisProcess* this = xCalloc(1, sizeof(SolarisProcess)); Object_setClass(this, Class(SolarisProcess)); - Process_init(&this->super, settings); + Process_init(&this->super, host); return &this->super; } diff --git a/solaris/SolarisProcess.h b/solaris/SolarisProcess.h index 13a2bc1fc..6bb3ca1ce 100644 --- a/solaris/SolarisProcess.h +++ b/solaris/SolarisProcess.h @@ -21,7 +21,7 @@ in the source distribution for its full text. #undef ERR #define ERR (-1) -#include "Settings.h" +#include "Machine.h" typedef struct SolarisProcess_ { @@ -42,7 +42,7 @@ extern const ProcessClass SolarisProcess_class; extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD]; -Process* SolarisProcess_new(const Settings* settings); +Process* SolarisProcess_new(const Machine* host); void Process_delete(Object* cast); diff --git a/solaris/SolarisProcessList.c b/solaris/SolarisProcessList.c index 409079895..84991d6e1 100644 --- a/solaris/SolarisProcessList.c +++ b/solaris/SolarisProcessList.c @@ -48,6 +48,7 @@ static char* SolarisProcessList_readZoneName(kstat_ctl_t* kd, SolarisProcess* sp } static void SolarisProcessList_updateCPUcount(ProcessList* super) { + Machine* host = super->host; SolarisProcessList* spl = (SolarisProcessList*) super; long int s; bool change = false; @@ -56,7 +57,7 @@ static void SolarisProcessList_updateCPUcount(ProcessList* super) { if (s < 1) CRT_fatalError("Cannot get existing CPU count by sysconf(_SC_NPROCESSORS_CONF)"); - if (s != super->existingCPUs) { + if (s != host->existingCPUs) { if (s == 1) { spl->cpus = xRealloc(spl->cpus, sizeof(CPUData)); spl->cpus[0].online = true; @@ -69,16 +70,16 @@ static void SolarisProcessList_updateCPUcount(ProcessList* super) { } change = true; - super->existingCPUs = s; + host->existingCPUs = s; } s = sysconf(_SC_NPROCESSORS_ONLN); if (s < 1) CRT_fatalError("Cannot get active CPU count by sysconf(_SC_NPROCESSORS_ONLN)"); - if (s != super->activeCPUs) { + if (s != host->activeCPUs) { change = true; - super->activeCPUs = s; + host->activeCPUs = s; } if (change) { @@ -89,10 +90,10 @@ static void SolarisProcessList_updateCPUcount(ProcessList* super) { } } -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) { +ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList) { SolarisProcessList* spl = xCalloc(1, sizeof(SolarisProcessList)); ProcessList* pl = (ProcessList*) spl; - ProcessList_init(pl, Class(SolarisProcess), usersTable, pidMatchList, userId); + ProcessList_init(pl, Class(SolarisProcess), host, pidMatchList); spl->kd = kstat_open(); if (!spl->kd) @@ -110,8 +111,8 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, ui static inline void SolarisProcessList_scanCPUTime(ProcessList* pl) { const SolarisProcessList* spl = (SolarisProcessList*) pl; - unsigned int activeCPUs = pl->activeCPUs; - unsigned int existingCPUs = pl->existingCPUs; + unsigned int activeCPUs = pl->host->activeCPUs; + unsigned int existingCPUs = pl->host->existingCPUs; kstat_t* cpuinfo = NULL; kstat_named_t* idletime = NULL; kstat_named_t* intrtime = NULL; @@ -203,7 +204,9 @@ static inline void SolarisProcessList_scanCPUTime(ProcessList* pl) { } static inline void SolarisProcessList_scanMemoryInfo(ProcessList* pl) { + Machine* host = pl->host; SolarisProcessList* spl = (SolarisProcessList*) pl; + static kstat_t *meminfo = NULL; int ksrphyserr = -1; kstat_named_t *totalmem_pgs = NULL; @@ -230,23 +233,23 @@ static inline void SolarisProcessList_scanMemoryInfo(ProcessList* pl) { freemem_pgs = kstat_data_lookup_wrapper(meminfo, "freemem"); pages = kstat_data_lookup_wrapper(meminfo, "pagestotal"); - pl->totalMem = totalmem_pgs->value.ui64 * pageSizeKB; - if (pl->totalMem > freemem_pgs->value.ui64 * pageSizeKB) { - pl->usedMem = pl->totalMem - freemem_pgs->value.ui64 * pageSizeKB; + host->totalMem = totalmem_pgs->value.ui64 * pageSizeKB; + if (host->totalMem > freemem_pgs->value.ui64 * pageSizeKB) { + host->usedMem = host->totalMem - freemem_pgs->value.ui64 * pageSizeKB; } else { - pl->usedMem = 0; // This can happen in non-global zone (in theory) + host->usedMem = 0; // This can happen in non-global zone (in theory) } // Not sure how to implement this on Solaris - suggestions welcome! - pl->cachedMem = 0; + host->cachedMem = 0; // Not really "buffers" but the best Solaris analogue that I can find to // "memory in use but not by programs or the kernel itself" - pl->buffersMem = (totalmem_pgs->value.ui64 - pages->value.ui64) * pageSizeKB; + host->buffersMem = (totalmem_pgs->value.ui64 - pages->value.ui64) * pageSizeKB; } else { // Fall back to basic sysconf if kstat isn't working - pl->totalMem = sysconf(_SC_PHYS_PAGES) * pageSize; - pl->buffersMem = 0; - pl->cachedMem = 0; - pl->usedMem = pl->totalMem - (sysconf(_SC_AVPHYS_PAGES) * pageSize); + host->totalMem = sysconf(_SC_PHYS_PAGES) * pageSize; + host->buffersMem = 0; + host->cachedMem = 0; + host->usedMem = host->totalMem - (sysconf(_SC_AVPHYS_PAGES) * pageSize); } // Part 2 - swap @@ -276,8 +279,8 @@ static inline void SolarisProcessList_scanMemoryInfo(ProcessList* pl) { } free(spathbase); free(sl); - pl->totalSwap = totalswap * pageSizeKB; - pl->usedSwap = pl->totalSwap - (totalfree * pageSizeKB); + host->totalSwap = totalswap * pageSizeKB; + host->usedSwap = host->totalSwap - (totalfree * pageSizeKB); } static inline void SolarisProcessList_scanZfsArcstats(ProcessList* pl) { @@ -389,6 +392,7 @@ static int SolarisProcessList_walkproc(psinfo_t* _psinfo, lwpsinfo_t* _lwpsinfo, // Setup process list ProcessList* pl = (ProcessList*) listptr; SolarisProcessList* spl = (SolarisProcessList*) listptr; + Machine* host = pl->super.host; id_t lwpid_real = _lwpsinfo->pr_lwpid; if (lwpid_real > 1023) { @@ -438,7 +442,7 @@ static int SolarisProcessList_walkproc(psinfo_t* _psinfo, lwpsinfo_t* _lwpsinfo, if (proc->st_uid != _psinfo->pr_euid) { proc->st_uid = _psinfo->pr_euid; - proc->user = UsersTable_getRef(pl->usersTable, proc->st_uid); + proc->user = UsersTable_getRef(host->usersTable, proc->st_uid); } if (!preExisting) { @@ -551,10 +555,20 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { proc_walk(&SolarisProcessList_walkproc, super, PR_WALK_LWP); } -bool ProcessList_isCPUonline(const ProcessList* super, unsigned int id) { - assert(id < super->existingCPUs); +Machine* Machine_new(UsersTable* usersTable, uid_t userId) { + Machine* this = xCalloc(1, sizeof(Machine)); + Machine_init(this, usersTable, userId); + return this; +} + +void Machine_delete(Machine* host) { + free(host); +} + +bool Machine_isCPUonline(const Machine* host, unsigned int id) { + assert(id < host->existingCPUs); - const SolarisProcessList* spl = (const SolarisProcessList*) super; + const SolarisProcessList* spl = (const SolarisProcessList*) host->pl; - return (super->existingCPUs == 1) ? true : spl->cpus[id + 1].online; + return (super->host->existingCPUs == 1) ? true : spl->cpus[id + 1].online; } diff --git a/solaris/SolarisProcessList.h b/solaris/SolarisProcessList.h index e2f4f683f..7cb641673 100644 --- a/solaris/SolarisProcessList.h +++ b/solaris/SolarisProcessList.h @@ -54,12 +54,16 @@ typedef struct SolarisProcessList_ { ZfsArcStats zfs; } SolarisProcessList; -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId); +ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList); void ProcessList_delete(ProcessList* pl); void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate); -bool ProcessList_isCPUonline(const ProcessList* super, unsigned int id); +Machine* Machine_new(UsersTable* usersTable, uid_t userId); + +bool Machine_isCPUonline(const Machine* host, unsigned int id); + +void Machine_delete(Machine* host); #endif diff --git a/unsupported/UnsupportedProcess.c b/unsupported/UnsupportedProcess.c index 2aca0488a..4d8cb0803 100644 --- a/unsupported/UnsupportedProcess.c +++ b/unsupported/UnsupportedProcess.c @@ -44,10 +44,10 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = { [TGID] = { .name = "TGID", .title = "TGID", .description = "Thread group ID (i.e. process ID)", .flags = 0, .pidColumn = true, }, }; -Process* UnsupportedProcess_new(const Settings* settings) { +Process* UnsupportedProcess_new(const Machine* host) { Process* this = xCalloc(1, sizeof(UnsupportedProcess)); Object_setClass(this, Class(UnsupportedProcess)); - Process_init(this, settings); + Process_init(this, host); return this; } @@ -60,7 +60,7 @@ void Process_delete(Object* cast) { static void UnsupportedProcess_writeField(const Process* this, RichString* str, ProcessField field) { const UnsupportedProcess* up = (const UnsupportedProcess*) this; - bool coloring = this->settings->highlightMegabytes; + bool coloring = this->host->settings->highlightMegabytes; char buffer[256]; buffer[255] = '\0'; int attr = CRT_colors[DEFAULT_COLOR]; size_t n = sizeof(buffer) - 1; diff --git a/unsupported/UnsupportedProcess.h b/unsupported/UnsupportedProcess.h index e30169c5d..21956ddda 100644 --- a/unsupported/UnsupportedProcess.h +++ b/unsupported/UnsupportedProcess.h @@ -7,7 +7,7 @@ Released under the GNU GPLv2+, see the COPYING file in the source distribution for its full text. */ -#include "Settings.h" +#include "Machine.h" typedef struct UnsupportedProcess_ { @@ -19,7 +19,7 @@ typedef struct UnsupportedProcess_ { extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD]; -Process* UnsupportedProcess_new(const Settings* settings); +Process* UnsupportedProcess_new(const Machine* host); void Process_delete(Object* cast); diff --git a/unsupported/UnsupportedProcessList.c b/unsupported/UnsupportedProcessList.c index fc226f76f..e5b7af6d3 100644 --- a/unsupported/UnsupportedProcessList.c +++ b/unsupported/UnsupportedProcessList.c @@ -14,12 +14,12 @@ in the source distribution for its full text. #include "UnsupportedProcess.h" -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) { +ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList) { ProcessList* this = xCalloc(1, sizeof(ProcessList)); - ProcessList_init(this, Class(Process), usersTable, pidMatchList, userId); + ProcessList_init(this, Class(Process), host, pidMatchList); - this->existingCPUs = 1; - this->activeCPUs = 1; + host->existingCPUs = 1; + host->activeCPUs = 1; return this; } @@ -91,10 +91,20 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { ProcessList_add(super, proc); } -bool ProcessList_isCPUonline(const ProcessList* super, unsigned int id) { - assert(id < super->existingCPUs); +Machine* Machine_new(UsersTable* usersTable, uid_t userId) { + Machine* this = xCalloc(1, sizeof(Machine)); + Machine_init(this, usersTable, userId); + return this; +} + +void Machine_delete(Machine* host) { + free(host); +} + +bool Machine_isCPUonline(const Machine* host, unsigned int id) { + assert(id < host->existingCPUs); - (void) super; (void) id; + (void) host; (void) id; return true; } diff --git a/unsupported/UnsupportedProcessList.h b/unsupported/UnsupportedProcessList.h index 9f4d23aae..cdb0b6015 100644 --- a/unsupported/UnsupportedProcessList.h +++ b/unsupported/UnsupportedProcessList.h @@ -10,12 +10,16 @@ in the source distribution for its full text. #include "ProcessList.h" -ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId); +ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList); void ProcessList_delete(ProcessList* this); void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate); -bool ProcessList_isCPUonline(const ProcessList* super, unsigned int id); +Machine* Machine_new(UsersTable* usersTable, uid_t userId); + +bool Machine_isCPUonline(const Machine* host, unsigned int id); + +void Machine_delete(Machine* host); #endif From 72235d8e098d9d79029dca65122605741e1aafad Mon Sep 17 00:00:00 2001 From: Nathan Scott Date: Tue, 2 May 2023 16:56:18 +1000 Subject: [PATCH 089/106] Adapt platform code for the new Machine base class Move host-centric data to new derived Machine classes, separate from process-list-centric data. --- CommandLine.c | 6 +- Machine.h | 2 + Makefile.am | 18 + ProcessList.c | 10 +- ProcessList.h | 6 +- ScreenManager.c | 7 +- darwin/DarwinMachine.c | 113 ++++ darwin/DarwinMachine.h | 28 + darwin/DarwinProcess.c | 5 +- darwin/DarwinProcessList.c | 102 +--- darwin/DarwinProcessList.h | 21 - darwin/Platform.c | 23 +- dragonflybsd/DragonFlyBSDMachine.c | 341 +++++++++++ dragonflybsd/DragonFlyBSDMachine.h | 61 ++ dragonflybsd/DragonFlyBSDProcessList.c | 353 +----------- dragonflybsd/DragonFlyBSDProcessList.h | 47 -- dragonflybsd/Platform.c | 27 +- freebsd/FreeBSDMachine.c | 396 +++++++++++++ freebsd/FreeBSDMachine.h | 54 ++ freebsd/FreeBSDProcessList.c | 386 +------------ freebsd/FreeBSDProcessList.h | 41 -- freebsd/Platform.c | 32 +- linux/HugePageMeter.c | 8 +- linux/LibSensors.c | 2 +- linux/LibSensors.h | 2 +- linux/LinuxMachine.c | 707 +++++++++++++++++++++++ linux/LinuxMachine.h | 110 ++++ linux/LinuxProcess.c | 14 +- linux/LinuxProcessList.c | 763 ++----------------------- linux/LinuxProcessList.h | 92 --- linux/Platform.c | 53 +- netbsd/NetBSDMachine.c | 282 +++++++++ netbsd/NetBSDMachine.h | 54 ++ netbsd/NetBSDProcessList.c | 280 +-------- netbsd/NetBSDProcessList.h | 40 -- netbsd/Platform.c | 7 +- openbsd/OpenBSDMachine.c | 286 +++++++++ openbsd/OpenBSDMachine.h | 53 ++ openbsd/OpenBSDProcessList.c | 299 +--------- openbsd/OpenBSDProcessList.h | 43 -- openbsd/Platform.c | 9 +- pcp/PCPMachine.c | 332 +++++++++++ pcp/PCPMachine.h | 68 +++ pcp/PCPProcessList.c | 369 ++---------- pcp/PCPProcessList.h | 52 -- pcp/Platform.c | 32 +- solaris/Platform.c | 21 +- solaris/SolarisMachine.c | 332 +++++++++++ solaris/SolarisMachine.h | 59 ++ solaris/SolarisProcessList.c | 332 +---------- solaris/SolarisProcessList.h | 36 -- unsupported/UnsupportedMachine.c | 56 ++ unsupported/UnsupportedMachine.h | 17 + unsupported/UnsupportedProcessList.c | 35 +- unsupported/UnsupportedProcessList.h | 14 +- 55 files changed, 3672 insertions(+), 3266 deletions(-) create mode 100644 darwin/DarwinMachine.c create mode 100644 darwin/DarwinMachine.h create mode 100644 dragonflybsd/DragonFlyBSDMachine.c create mode 100644 dragonflybsd/DragonFlyBSDMachine.h create mode 100644 freebsd/FreeBSDMachine.c create mode 100644 freebsd/FreeBSDMachine.h create mode 100644 linux/LinuxMachine.c create mode 100644 linux/LinuxMachine.h create mode 100644 netbsd/NetBSDMachine.c create mode 100644 netbsd/NetBSDMachine.h create mode 100644 openbsd/OpenBSDMachine.c create mode 100644 openbsd/OpenBSDMachine.h create mode 100644 pcp/PCPMachine.c create mode 100644 pcp/PCPMachine.h create mode 100644 solaris/SolarisMachine.c create mode 100644 solaris/SolarisMachine.h create mode 100644 unsupported/UnsupportedMachine.c create mode 100644 unsupported/UnsupportedMachine.h diff --git a/CommandLine.c b/CommandLine.c index 9e2018efe..8095fa8f8 100644 --- a/CommandLine.c +++ b/CommandLine.c @@ -383,9 +383,11 @@ int CommandLine_run(int argc, char** argv) { ScreenManager* scr = ScreenManager_new(header, host, &state, true); ScreenManager_add(scr, (Panel*) panel, -1); - ProcessList_scan(pl, false); + Machine_scan(host); + ProcessList_scan(pl); CommandLine_delay(host, 75); - ProcessList_scan(pl, false); + Machine_scan(host); + ProcessList_scan(pl); if (settings->ss->allBranchesCollapsed) ProcessList_collapseAllBranches(pl); diff --git a/Machine.h b/Machine.h index 3683701a3..4628d686c 100644 --- a/Machine.h +++ b/Machine.h @@ -86,4 +86,6 @@ bool Machine_isCPUonline(const Machine* this, unsigned int id); void Machine_addList(Machine* this, struct ProcessList_ *pl); +void Machine_scan(Machine* this); + #endif diff --git a/Makefile.am b/Makefile.am index 94db4d77e..b25d1cb82 100644 --- a/Makefile.am +++ b/Makefile.am @@ -166,6 +166,7 @@ linux_platform_headers = \ linux/IOPriority.h \ linux/IOPriorityPanel.h \ linux/LibSensors.h \ + linux/LinuxMachine.h \ linux/LinuxProcess.h \ linux/LinuxProcessList.h \ linux/Platform.h \ @@ -188,6 +189,7 @@ linux_platform_sources = \ linux/HugePageMeter.c \ linux/IOPriorityPanel.c \ linux/LibSensors.c \ + linux/LinuxMachine.c \ linux/LinuxProcess.c \ linux/LinuxProcessList.c \ linux/Platform.c \ @@ -208,6 +210,7 @@ endif # ------- freebsd_platform_headers = \ + freebsd/FreeBSDMachine.h \ freebsd/FreeBSDProcessList.h \ freebsd/FreeBSDProcess.h \ freebsd/Platform.h \ @@ -223,6 +226,7 @@ freebsd_platform_headers = \ freebsd_platform_sources = \ freebsd/Platform.c \ + freebsd/FreeBSDMachine.c \ freebsd/FreeBSDProcessList.c \ freebsd/FreeBSDProcess.c \ generic/fdstat_sysctl.c \ @@ -242,6 +246,7 @@ endif # ------------ dragonflybsd_platform_headers = \ + dragonflybsd/DragonFlyBSDMachine.h \ dragonflybsd/DragonFlyBSDProcessList.h \ dragonflybsd/DragonFlyBSDProcess.h \ dragonflybsd/Platform.h \ @@ -252,6 +257,7 @@ dragonflybsd_platform_headers = \ generic/uname.h dragonflybsd_platform_sources = \ + dragonflybsd/DragonFlyBSDMachine.c \ dragonflybsd/DragonFlyBSDProcessList.c \ dragonflybsd/DragonFlyBSDProcess.c \ dragonflybsd/Platform.c \ @@ -275,6 +281,7 @@ netbsd_platform_headers = \ generic/uname.h \ netbsd/Platform.h \ netbsd/ProcessField.h \ + netbsd/NetBSDMachine.h \ netbsd/NetBSDProcess.h \ netbsd/NetBSDProcessList.h @@ -284,6 +291,7 @@ netbsd_platform_sources = \ generic/hostname.c \ generic/uname.c \ netbsd/Platform.c \ + netbsd/NetBSDMachine.c \ netbsd/NetBSDProcess.c \ netbsd/NetBSDProcessList.c @@ -299,6 +307,7 @@ openbsd_platform_headers = \ generic/gettime.h \ generic/hostname.h \ generic/uname.h \ + openbsd/OpenBSDMachine.h \ openbsd/OpenBSDProcessList.h \ openbsd/OpenBSDProcess.h \ openbsd/Platform.h \ @@ -308,6 +317,7 @@ openbsd_platform_sources = \ generic/gettime.c \ generic/hostname.c \ generic/uname.c \ + openbsd/OpenBSDMachine.c \ openbsd/OpenBSDProcessList.c \ openbsd/OpenBSDProcess.c \ openbsd/Platform.c @@ -321,6 +331,7 @@ endif # ------ darwin_platform_headers = \ + darwin/DarwinMachine.h \ darwin/DarwinProcess.h \ darwin/DarwinProcessList.h \ darwin/Platform.h \ @@ -338,6 +349,7 @@ darwin_platform_headers = \ darwin_platform_sources = \ darwin/Platform.c \ darwin/PlatformHelpers.c \ + darwin/DarwinMachine.c \ darwin/DarwinProcess.c \ darwin/DarwinProcessList.c \ generic/fdstat_sysctl.c \ @@ -363,6 +375,7 @@ solaris_platform_headers = \ generic/uname.h \ solaris/ProcessField.h \ solaris/Platform.h \ + solaris/SolarisMachine.h \ solaris/SolarisProcess.h \ solaris/SolarisProcessList.h \ zfs/ZfsArcMeter.h \ @@ -374,6 +387,7 @@ solaris_platform_sources = \ generic/hostname.c \ generic/uname.c \ solaris/Platform.c \ + solaris/SolarisMachine.c \ solaris/SolarisProcess.c \ solaris/SolarisProcessList.c \ zfs/ZfsArcMeter.c \ @@ -393,6 +407,7 @@ pcp_platform_headers = \ linux/ZramStats.h \ pcp/PCPDynamicColumn.h \ pcp/PCPDynamicMeter.h \ + pcp/PCPMachine.h \ pcp/PCPMetric.h \ pcp/PCPProcess.h \ pcp/PCPProcessList.h \ @@ -407,6 +422,7 @@ pcp_platform_sources = \ linux/ZramMeter.c \ pcp/PCPDynamicColumn.c \ pcp/PCPDynamicMeter.c \ + pcp/PCPMachine.c \ pcp/PCPMetric.c \ pcp/PCPProcess.c \ pcp/PCPProcessList.c \ @@ -427,12 +443,14 @@ unsupported_platform_headers = \ generic/gettime.h \ unsupported/Platform.h \ unsupported/ProcessField.h \ + unsupported/UnsupportedMachine.h \ unsupported/UnsupportedProcess.h \ unsupported/UnsupportedProcessList.h unsupported_platform_sources = \ generic/gettime.c \ unsupported/Platform.c \ + unsupported/UnsupportedMachine.c \ unsupported/UnsupportedProcess.c \ unsupported/UnsupportedProcessList.c diff --git a/ProcessList.c b/ProcessList.c index 49217b8c7..58e63d030 100644 --- a/ProcessList.c +++ b/ProcessList.c @@ -410,13 +410,7 @@ Process* ProcessList_getProcess(ProcessList* this, pid_t pid, bool* preExisting, return proc; } -void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate) { - // in pause mode only gather global data for meters (CPU/memory/...) - if (pauseProcessUpdate) { - ProcessList_goThroughEntries(this, true); - return; - } - +void ProcessList_scan(ProcessList* this) { // mark all process as "dirty" for (int i = 0; i < Vector_size(this->processes); i++) { Process* p = (Process*) Vector_get(this->processes, i); @@ -442,7 +436,7 @@ void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate) { firstScanDone = true; } - ProcessList_goThroughEntries(this, false); + ProcessList_goThroughEntries(this); uid_t maxUid = 0; const Settings* settings = host->settings; diff --git a/ProcessList.h b/ProcessList.h index d09cc0723..0f0f7d517 100644 --- a/ProcessList.h +++ b/ProcessList.h @@ -49,8 +49,8 @@ typedef struct ProcessList_ { /* Implemented by platforms */ ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList); -void ProcessList_delete(ProcessList* pl); -void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate); +void ProcessList_delete(ProcessList* this); +void ProcessList_goThroughEntries(ProcessList* this); void ProcessList_init(ProcessList* this, const ObjectClass* klass, Machine* host, Hashtable* pidMatchList); @@ -74,7 +74,7 @@ void ProcessList_rebuildPanel(ProcessList* this); Process* ProcessList_getProcess(ProcessList* this, pid_t pid, bool* preExisting, Process_New constructor); -void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate); +void ProcessList_scan(ProcessList* this); static inline Process* ProcessList_findProcess(ProcessList* this, pid_t pid) { return (Process*) Hashtable_get(this->processTable, pid); diff --git a/ScreenManager.c b/ScreenManager.c index f1897893e..18e09343e 100644 --- a/ScreenManager.c +++ b/ScreenManager.c @@ -16,6 +16,7 @@ in the source distribution for its full text. #include "CRT.h" #include "FunctionBar.h" +#include "Machine.h" #include "Macros.h" #include "Object.h" #include "Platform.h" @@ -135,8 +136,10 @@ static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTi host->pl->needsSort = true; *sortTimeout = 1; } - // scan processes first - some header values are calculated there - ProcessList_scan(host->pl, this->state->pauseUpdate); + // sample current values for system metrics and processes if not paused + Machine_scan(host); + if (!this->state->pauseUpdate) + ProcessList_scan(host->pl); // always update header, especially to avoid gaps in graph meters Header_updateData(this->header); // force redraw if the number of UID digits was changed diff --git a/darwin/DarwinMachine.c b/darwin/DarwinMachine.c new file mode 100644 index 000000000..6bf52b76a --- /dev/null +++ b/darwin/DarwinMachine.c @@ -0,0 +1,113 @@ +/* +htop - DarwinMachine.c +(C) 2014 Hisham H. Muhammad +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "darwin/DarwinMachine.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "CRT.h" +#include "Machine.h" +#include "darwin/Platform.h" +#include "darwin/PlatformHelpers.h" +#include "generic/openzfs_sysctl.h" +#include "zfs/ZfsArcStats.h" + + +static void DarwinMachine_getHostInfo(host_basic_info_data_t* p) { + mach_msg_type_number_t info_size = HOST_BASIC_INFO_COUNT; + + if (0 != host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t)p, &info_size)) { + CRT_fatalError("Unable to retrieve host info"); + } +} + +static void DarwinMachine_freeCPULoadInfo(processor_cpu_load_info_t* p) { + if (NULL != p && NULL != *p) { + if (0 != munmap(*p, vm_page_size)) { + CRT_fatalError("Unable to free old CPU load information"); + } + *p = NULL; + } +} + +static unsigned DarwinMachine_allocateCPULoadInfo(processor_cpu_load_info_t* p) { + mach_msg_type_number_t info_size = sizeof(processor_cpu_load_info_t); + unsigned cpu_count; + + // TODO Improving the accuracy of the load counts would help a lot. + if (0 != host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &cpu_count, (processor_info_array_t*)p, &info_size)) { + CRT_fatalError("Unable to retrieve CPU info"); + } + + return cpu_count; +} + +static void DarwinMachine_getVMStats(vm_statistics_t p) { + mach_msg_type_number_t info_size = HOST_VM_INFO_COUNT; + + if (host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)p, &info_size) != 0) { + CRT_fatalError("Unable to retrieve VM statistics"); + } +} + +void Machine_scan(Machine* super) { + DarwinMachine* host = (DarwinMachine*) super; + + /* Update the global data (CPU times and VM stats) */ + DarwinMachine_freeCPULoadInfo(&host->prev_load); + host->prev_load = host->curr_load; + DarwinMachine_allocateCPULoadInfo(&host->curr_load); + DarwinMachine_getVMStats(&host->vm_stats); + openzfs_sysctl_updateArcStats(&host->zfs); +} + +Machine* Machine_new(UsersTable* usersTable, uid_t userId) { + DarwinMachine* this = xCalloc(1, sizeof(DarwinMachine)); + Machine* super = &this->super; + + Machine_init(super, usersTable, userId); + + /* Initialize the CPU information */ + super->activeCPUs = DarwinMachine_allocateCPULoadInfo(&this->prev_load); + super->existingCPUs = super->activeCPUs; + DarwinMachine_getHostInfo(&this->host_info); + DarwinMachine_allocateCPULoadInfo(&this->curr_load); + + /* Initialize the VM statistics */ + DarwinMachine_getVMStats(&this->vm_stats); + + /* Initialize the ZFS kstats, if zfs.kext loaded */ + openzfs_sysctl_init(&this->zfs); + openzfs_sysctl_updateArcStats(&this->zfs); + + return super; +} + +void Machine_delete(Machine* super) { + DarwinMachine* host = (DarwinMachine*) super; + + DarwinMachine_freeCPULoadInfo(&host->prev_load); + + Machine_done(super); + free(super); +} + +bool Machine_isCPUonline(const Machine* host, unsigned int id) { + assert(id < host->existingCPUs); + + // TODO: support offline CPUs and hot swapping + (void) host; (void) id; + + return true; +} diff --git a/darwin/DarwinMachine.h b/darwin/DarwinMachine.h new file mode 100644 index 000000000..3135b5895 --- /dev/null +++ b/darwin/DarwinMachine.h @@ -0,0 +1,28 @@ +#ifndef HEADER_DarwinMachine +#define HEADER_DarwinMachine +/* +htop - DarwinMachine.h +(C) 2014 Hisham H. Muhammad +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include +#include + +#include "Machine.h" +#include "zfs/ZfsArcStats.h" + + +typedef struct DarwinMachine_ { + Machine super; + + host_basic_info_data_t host_info; + vm_statistics_data_t vm_stats; + processor_cpu_load_info_t prev_load; + processor_cpu_load_info_t curr_load; + + ZfsArcStats zfs; +} DarwinMachine; + +#endif diff --git a/darwin/DarwinProcess.c b/darwin/DarwinProcess.c index 22004e36b..e6366d70f 100644 --- a/darwin/DarwinProcess.c +++ b/darwin/DarwinProcess.c @@ -16,6 +16,7 @@ in the source distribution for its full text. #include "CRT.h" #include "Process.h" +#include "darwin/DarwinMachine.h" #include "darwin/Platform.h" @@ -365,6 +366,8 @@ void DarwinProcess_setFromLibprocPidinfo(DarwinProcess* proc, DarwinProcessList* struct proc_taskinfo pti; if (sizeof(pti) == proc_pidinfo(proc->super.pid, PROC_PIDTASKINFO, 0, &pti, sizeof(pti))) { + const DarwinMachine* dhost = (const DarwinMachine*) proc->super.host; + uint64_t total_existing_time_ns = proc->stime + proc->utime; uint64_t user_time_ns = Platform_machTicksToNanoseconds(pti.pti_total_user); @@ -386,7 +389,7 @@ void DarwinProcess_setFromLibprocPidinfo(DarwinProcess* proc, DarwinProcessList* proc->super.m_resident = pti.pti_resident_size / ONE_K; proc->super.majflt = pti.pti_faults; proc->super.percent_mem = (double)pti.pti_resident_size * 100.0 - / (double)dpl->host_info.max_mem; + / (double)dhost->host_info.max_mem; proc->stime = system_time_ns; proc->utime = user_time_ns; diff --git a/darwin/DarwinProcessList.c b/darwin/DarwinProcessList.c index 16c44d2cb..bf311dc7a 100644 --- a/darwin/DarwinProcessList.c +++ b/darwin/DarwinProcessList.c @@ -20,6 +20,7 @@ in the source distribution for its full text. #include "CRT.h" #include "ProcessList.h" +#include "darwin/DarwinMachine.h" #include "darwin/DarwinProcess.h" #include "darwin/Platform.h" #include "darwin/PlatformHelpers.h" @@ -27,43 +28,6 @@ in the source distribution for its full text. #include "zfs/ZfsArcStats.h" -static void ProcessList_getHostInfo(host_basic_info_data_t* p) { - mach_msg_type_number_t info_size = HOST_BASIC_INFO_COUNT; - - if (0 != host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t)p, &info_size)) { - CRT_fatalError("Unable to retrieve host info"); - } -} - -static void ProcessList_freeCPULoadInfo(processor_cpu_load_info_t* p) { - if (NULL != p && NULL != *p) { - if (0 != munmap(*p, vm_page_size)) { - CRT_fatalError("Unable to free old CPU load information"); - } - *p = NULL; - } -} - -static unsigned ProcessList_allocateCPULoadInfo(processor_cpu_load_info_t* p) { - mach_msg_type_number_t info_size = sizeof(processor_cpu_load_info_t); - unsigned cpu_count; - - // TODO Improving the accuracy of the load counts woule help a lot. - if (0 != host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &cpu_count, (processor_info_array_t*)p, &info_size)) { - CRT_fatalError("Unable to retrieve CPU info"); - } - - return cpu_count; -} - -static void ProcessList_getVMStats(vm_statistics_t p) { - mach_msg_type_number_t info_size = HOST_VM_INFO_COUNT; - - if (host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)p, &info_size) != 0) { - CRT_fatalError("Unable to retrieve VM statistics"); - } -} - static struct kinfo_proc* ProcessList_getKInfoProcs(size_t* count) { int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0 }; struct kinfo_proc* processes = NULL; @@ -91,29 +55,11 @@ static struct kinfo_proc* ProcessList_getKInfoProcs(size_t* count) { ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList) { DarwinProcessList* this = xCalloc(1, sizeof(DarwinProcessList)); + ProcessList* super = &this->super; - ProcessList_init(&this->super, Class(DarwinProcess), host, pidMatchList); - - /* Initialize the CPU information */ - host->activeCPUs = ProcessList_allocateCPULoadInfo(&this->prev_load); - // TODO: support offline CPUs and hot swapping - host->existingCPUs = host->activeCPUs; - ProcessList_getHostInfo(&this->host_info); - ProcessList_allocateCPULoadInfo(&this->curr_load); - - /* Initialize the VM statistics */ - ProcessList_getVMStats(&this->vm_stats); - - /* Initialize the ZFS kstats, if zfs.kext loaded */ - openzfs_sysctl_init(&this->zfs); - openzfs_sysctl_updateArcStats(&this->zfs); + ProcessList_init(super, Class(DarwinProcess), host, pidMatchList); - this->super.kernelThreads = 0; - this->super.userlandThreads = 0; - this->super.totalTasks = 0; - this->super.runningTasks = 0; - - return &this->super; + return super; } void ProcessList_delete(ProcessList* this) { @@ -121,31 +67,20 @@ void ProcessList_delete(ProcessList* this) { free(this); } -void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { - DarwinProcessList* dpl = (DarwinProcessList*)super; +void ProcessList_goThroughEntries(ProcessList* super) { const Machine* host = super->host; + const DarwinMachine* dhost = (const DarwinMachine*) host; + DarwinProcessList* dpl = (DarwinProcessList*) super; bool preExisting = true; struct kinfo_proc* ps; size_t count; DarwinProcess* proc; - /* Update the global data (CPU times and VM stats) */ - ProcessList_freeCPULoadInfo(&dpl->prev_load); - dpl->prev_load = dpl->curr_load; - ProcessList_allocateCPULoadInfo(&dpl->curr_load); - ProcessList_getVMStats(&dpl->vm_stats); - openzfs_sysctl_updateArcStats(&dpl->zfs); - - // in pause mode only gather global data for meters (CPU/memory/...) - if (pauseProcessUpdate) { - return; - } - /* Get the time difference */ dpl->global_diff = 0; for (unsigned int i = 0; i < host->existingCPUs; ++i) { for (size_t j = 0; j < CPU_STATE_MAX; ++j) { - dpl->global_diff += dpl->curr_load[i].cpu_ticks[j] - dpl->prev_load[i].cpu_ticks[j]; + dpl->global_diff += dhost->curr_load[i].cpu_ticks[j] - dhost->prev_load[i].cpu_ticks[j]; } } @@ -178,7 +113,7 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { } // Disabled for High Sierra due to bug in macOS High Sierra - bool isScanThreadSupported = !Platform_KernelVersionIsBetween((KernelVersion) {17, 0, 0}, (KernelVersion) {17, 5, 0}); + bool isScanThreadSupported = !Platform_KernelVersionIsBetween((KernelVersion) {17, 0, 0}, (KernelVersion) {17, 5, 0}); if (isScanThreadSupported) { DarwinProcess_scanThreads(proc); @@ -193,22 +128,3 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { free(ps); } - -Machine* Machine_new(UsersTable* usersTable, uid_t userId) { - Machine* this = xCalloc(1, sizeof(Machine)); - Machine_init(this, usersTable, userId); - return this; -} - -void Machine_delete(Machine* host) { - free(host); -} - -bool Machine_isCPUonline(const Machine* host, unsigned int id) { - assert(id < host->existingCPUs); - - // TODO: support offline CPUs and hot swapping - (void) host; (void) id; - - return true; -} diff --git a/darwin/DarwinProcessList.h b/darwin/DarwinProcessList.h index 128896aec..56d6c1b52 100644 --- a/darwin/DarwinProcessList.h +++ b/darwin/DarwinProcessList.h @@ -11,33 +11,12 @@ in the source distribution for its full text. #include #include "ProcessList.h" -#include "zfs/ZfsArcStats.h" typedef struct DarwinProcessList_ { ProcessList super; - host_basic_info_data_t host_info; - vm_statistics_data_t vm_stats; - processor_cpu_load_info_t prev_load; - processor_cpu_load_info_t curr_load; - uint64_t kernel_threads; - uint64_t user_threads; uint64_t global_diff; - - ZfsArcStats zfs; } DarwinProcessList; -ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList); - -void ProcessList_delete(ProcessList* this); - -void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate); - -Machine* Machine_new(UsersTable* usersTable, uid_t userId); - -bool Machine_isCPUonline(const Machine* host, unsigned int id); - -void Machine_delete(Machine* host); - #endif diff --git a/darwin/Platform.c b/darwin/Platform.c index 71d082479..bb1ae92f7 100644 --- a/darwin/Platform.c +++ b/darwin/Platform.c @@ -44,7 +44,7 @@ in the source distribution for its full text. #include "SysArchMeter.h" #include "TasksMeter.h" #include "UptimeMeter.h" -#include "darwin/DarwinProcessList.h" +#include "darwin/DarwinMachine.h" #include "darwin/PlatformHelpers.h" #include "generic/fdstat_sysctl.h" #include "zfs/ZfsArcMeter.h" @@ -257,9 +257,9 @@ double Platform_setCPUValues(Meter* mtr, unsigned int cpu) { return Platform_setCPUAverageValues(mtr); } - const DarwinProcessList* dpl = (const DarwinProcessList*)mtr->host->pl; - const processor_cpu_load_info_t prev = &dpl->prev_load[cpu - 1]; - const processor_cpu_load_info_t curr = &dpl->curr_load[cpu - 1]; + const DarwinMachine* dhost = (const DarwinMachine*) mtr->host; + const processor_cpu_load_info_t prev = &dhost->prev_load[cpu - 1]; + const processor_cpu_load_info_t curr = &dhost->curr_load[cpu - 1]; double total = 0; /* Take the sums */ @@ -286,12 +286,11 @@ double Platform_setCPUValues(Meter* mtr, unsigned int cpu) { } void Platform_setMemoryValues(Meter* mtr) { - const Machine* host = mtr->host; - const DarwinProcessList* dpl = (const DarwinProcessList*) host->pl; - const struct vm_statistics* vm = &dpl->vm_stats; + const DarwinMachine* dhost = (const DarwinMachine*) mtr->host; + const struct vm_statistics* vm = &dhost->vm_stats; double page_K = (double)vm_page_size / (double)1024; - mtr->total = dpl->host_info.max_mem / 1024; + mtr->total = dhost->host_info.max_mem / 1024; mtr->values[MEMORY_METER_USED] = (double)(vm->active_count + vm->wire_count) * page_K; mtr->values[MEMORY_METER_BUFFERS] = (double)vm->purgeable_count * page_K; // mtr->values[MEMORY_METER_SHARED] = "shared memory, like tmpfs and shm" @@ -313,15 +312,15 @@ void Platform_setSwapValues(Meter* mtr) { } void Platform_setZfsArcValues(Meter* this) { - const DarwinProcessList* dpl = (const DarwinProcessList*) this->host->pl; + const DarwinMachine* dhost = (const DarwinMachine*) this->host; - ZfsArcMeter_readStats(this, &(dpl->zfs)); + ZfsArcMeter_readStats(this, &dhost->zfs); } void Platform_setZfsCompressedArcValues(Meter* this) { - const DarwinProcessList* dpl = (const DarwinProcessList*) this->host->pl; + const DarwinMachine* dhost = (const DarwinMachine*) this->host; - ZfsCompressedArcMeter_readStats(this, &(dpl->zfs)); + ZfsCompressedArcMeter_readStats(this, &dhost->zfs); } char* Platform_getProcessEnv(pid_t pid) { diff --git a/dragonflybsd/DragonFlyBSDMachine.c b/dragonflybsd/DragonFlyBSDMachine.c new file mode 100644 index 000000000..fc7eb3b0d --- /dev/null +++ b/dragonflybsd/DragonFlyBSDMachine.c @@ -0,0 +1,341 @@ +/* +htop - DragonFlyBSDMachine.c +(C) 2014 Hisham H. Muhammad +(C) 2017 Diederik de Groot +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "dragonflybsd/DragonFlyBSDMachine.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "CRT.h" +#include "Macros.h" + +#include "dragonflybsd/DragonFlyBSDProcess.h" + + +static int MIB_hw_physmem[2]; +static int MIB_vm_stats_vm_v_page_count[4]; + +static int MIB_vm_stats_vm_v_wire_count[4]; +static int MIB_vm_stats_vm_v_active_count[4]; +static int MIB_vm_stats_vm_v_cache_count[4]; +static int MIB_vm_stats_vm_v_inactive_count[4]; +static int MIB_vm_stats_vm_v_free_count[4]; + +static int MIB_vfs_bufspace[2]; + +static int MIB_kern_cp_time[2]; +static int MIB_kern_cp_times[2]; + +Machine* Machine_new(UsersTable* usersTable, uid_t userId) { + size_t len; + char errbuf[_POSIX2_LINE_MAX]; + DragonFlyBSDMachine* this = xCalloc(1, sizeof(DragonFlyBSDMachine)); + Machine* super = &this->super; + + Machine_init(this, usersTable, userId); + + // physical memory in system: hw.physmem + // physical page size: hw.pagesize + // usable pagesize : vm.stats.vm.v_page_size + len = 2; sysctlnametomib("hw.physmem", MIB_hw_physmem, &len); + + len = sizeof(this->pageSize); + if (sysctlbyname("vm.stats.vm.v_page_size", &this->pageSize, &len, NULL, 0) == -1) + CRT_fatalError("Cannot get pagesize by sysctl"); + this->pageSizeKb = this->pageSize / ONE_K; + + // usable page count vm.stats.vm.v_page_count + // actually usable memory : vm.stats.vm.v_page_count * vm.stats.vm.v_page_size + len = 4; sysctlnametomib("vm.stats.vm.v_page_count", MIB_vm_stats_vm_v_page_count, &len); + + len = 4; sysctlnametomib("vm.stats.vm.v_wire_count", MIB_vm_stats_vm_v_wire_count, &len); + len = 4; sysctlnametomib("vm.stats.vm.v_active_count", MIB_vm_stats_vm_v_active_count, &len); + len = 4; sysctlnametomib("vm.stats.vm.v_cache_count", MIB_vm_stats_vm_v_cache_count, &len); + len = 4; sysctlnametomib("vm.stats.vm.v_inactive_count", MIB_vm_stats_vm_v_inactive_count, &len); + len = 4; sysctlnametomib("vm.stats.vm.v_free_count", MIB_vm_stats_vm_v_free_count, &len); + + len = 2; sysctlnametomib("vfs.bufspace", MIB_vfs_bufspace, &len); + + int cpus = 1; + len = sizeof(cpus); + if (sysctlbyname("hw.ncpu", &cpus, &len, NULL, 0) != 0) { + cpus = 1; + } + + size_t sizeof_cp_time_array = sizeof(unsigned long) * CPUSTATES; + len = 2; sysctlnametomib("kern.cp_time", MIB_kern_cp_time, &len); + this->cp_time_o = xCalloc(CPUSTATES, sizeof(unsigned long)); + this->cp_time_n = xCalloc(CPUSTATES, sizeof(unsigned long)); + len = sizeof_cp_time_array; + + // fetch initial single (or average) CPU clicks from kernel + sysctl(MIB_kern_cp_time, 2, this->cp_time_o, &len, NULL, 0); + + // on smp box, fetch rest of initial CPU's clicks + if (cpus > 1) { + len = 2; sysctlnametomib("kern.cp_times", MIB_kern_cp_times, &len); + this->cp_times_o = xCalloc(cpus, sizeof_cp_time_array); + this->cp_times_n = xCalloc(cpus, sizeof_cp_time_array); + len = cpus * sizeof_cp_time_array; + sysctl(MIB_kern_cp_times, 2, this->cp_times_o, &len, NULL, 0); + } + + super->existingCPUs = MAXIMUM(cpus, 1); + // TODO: support offline CPUs and hot swapping + super->activeCPUs = super->existingCPUs; + + if (cpus == 1 ) { + this->cpus = xRealloc(this->cpus, sizeof(CPUData)); + } else { + // on smp we need CPUs + 1 to store averages too (as kernel kindly provides that as well) + this->cpus = xRealloc(this->cpus, (super->existingCPUs + 1) * sizeof(CPUData)); + } + + len = sizeof(kernelFScale); + if (sysctlbyname("kern.fscale", &kernelFScale, &len, NULL, 0) == -1) { + //sane default for kernel provided CPU percentage scaling, at least on x86 machines, in case this sysctl call failed + kernelFScale = 2048; + } + + this->kd = kvm_openfiles(NULL, "/dev/null", NULL, 0, errbuf); + if (this->kd == NULL) { + CRT_fatalError("kvm_openfiles() failed"); + } + + return super; +} + +void Machine_delete(Machine* super) { + const DragonFlyBSDMachine* this = (DragonFlyBSDProcessList*) super; + + Machine_done(this); + + if (this->kd) { + kvm_close(this->kd); + } + + if (this->jails) { + Hashtable_delete(this->jails); + } + + free(this->cp_time_o); + free(this->cp_time_n); + free(this->cp_times_o); + free(this->cp_times_n); + free(this->cpus); + + free(this); +} + +static void DragonFlyBSDMachine_scanCPUTime(Machine* super) { + const DragonFlyBSDMachine* this = (DragonFlyBSDMachine*) super; + + unsigned int cpus = super->existingCPUs; // actual CPU count + unsigned int maxcpu = cpus; // max iteration (in case we have average + smp) + int cp_times_offset; + + assert(cpus > 0); + + size_t sizeof_cp_time_array; + + unsigned long* cp_time_n; // old clicks state + unsigned long* cp_time_o; // current clicks state + + unsigned long cp_time_d[CPUSTATES]; + double cp_time_p[CPUSTATES]; + + // get averages or single CPU clicks + sizeof_cp_time_array = sizeof(unsigned long) * CPUSTATES; + sysctl(MIB_kern_cp_time, 2, this->cp_time_n, &sizeof_cp_time_array, NULL, 0); + + // get rest of CPUs + if (cpus > 1) { + // on smp systems DragonFlyBSD kernel concats all CPU states into one long array in + // kern.cp_times sysctl OID + // we store averages in dfpl->cpus[0], and actual cores after that + maxcpu = cpus + 1; + sizeof_cp_time_array = cpus * sizeof(unsigned long) * CPUSTATES; + sysctl(MIB_kern_cp_times, 2, this->cp_times_n, &sizeof_cp_time_array, NULL, 0); + } + + for (unsigned int i = 0; i < maxcpu; i++) { + if (cpus == 1) { + // single CPU box + cp_time_n = this->cp_time_n; + cp_time_o = this->cp_time_o; + } else { + if (i == 0 ) { + // average + cp_time_n = this->cp_time_n; + cp_time_o = this->cp_time_o; + } else { + // specific smp cores + cp_times_offset = i - 1; + cp_time_n = this->cp_times_n + (cp_times_offset * CPUSTATES); + cp_time_o = this->cp_times_o + (cp_times_offset * CPUSTATES); + } + } + + // diff old vs new + unsigned long long total_o = 0; + unsigned long long total_n = 0; + unsigned long long total_d = 0; + for (int s = 0; s < CPUSTATES; s++) { + cp_time_d[s] = cp_time_n[s] - cp_time_o[s]; + total_o += cp_time_o[s]; + total_n += cp_time_n[s]; + } + + // totals + total_d = total_n - total_o; + if (total_d < 1 ) { + total_d = 1; + } + + // save current state as old and calc percentages + for (int s = 0; s < CPUSTATES; ++s) { + cp_time_o[s] = cp_time_n[s]; + cp_time_p[s] = ((double)cp_time_d[s]) / ((double)total_d) * 100; + } + + CPUData* cpuData = &(this->cpus[i]); + cpuData->userPercent = cp_time_p[CP_USER]; + cpuData->nicePercent = cp_time_p[CP_NICE]; + cpuData->systemPercent = cp_time_p[CP_SYS]; + cpuData->irqPercent = cp_time_p[CP_INTR]; + cpuData->systemAllPercent = cp_time_p[CP_SYS] + cp_time_p[CP_INTR]; + // this one is not really used, but we store it anyway + cpuData->idlePercent = cp_time_p[CP_IDLE]; + } +} + +static void DragonFlyBSDMachine_scanMemoryInfo(Machine* super) { + DragonFlyBSDMachine* this = (DragonFlyBSDProcessList*) super; + + // @etosan: + // memory counter relationships seem to be these: + // total = active + wired + inactive + cache + free + // htop_used (unavail to anybody) = active + wired + // htop_cache (for cache meter) = buffers + cache + // user_free (avail to procs) = buffers + inactive + cache + free + size_t len = sizeof(super->totalMem); + + //disabled for now, as it is always smaller than phycal amount of memory... + //...to avoid "where is my memory?" questions + //sysctl(MIB_vm_stats_vm_v_page_count, 4, &(pl->totalMem), &len, NULL, 0); + //pl->totalMem *= pageSizeKb; + sysctl(MIB_hw_physmem, 2, &(super->totalMem), &len, NULL, 0); + pl->totalMem /= 1024; + + sysctl(MIB_vm_stats_vm_v_active_count, 4, &(this->memActive), &len, NULL, 0); + this->memActive *= this->pageSizeKb; + + sysctl(MIB_vm_stats_vm_v_wire_count, 4, &(this->memWire), &len, NULL, 0); + this->memWire *= this->pageSizeKb; + + sysctl(MIB_vfs_bufspace, 2, &(super->buffersMem), &len, NULL, 0); + super->buffersMem /= 1024; + + sysctl(MIB_vm_stats_vm_v_cache_count, 4, &(super->cachedMem), &len, NULL, 0); + super->cachedMem *= this->pageSizeKb; + super->usedMem = this->memActive + this->memWire; + + struct kvm_swap swap[16]; + int nswap = kvm_getswapinfo(this->kd, swap, ARRAYSIZE(swap), 0); + super->totalSwap = 0; + super->usedSwap = 0; + for (int i = 0; i < nswap; i++) { + super->totalSwap += swap[i].ksw_total; + super->usedSwap += swap[i].ksw_used; + } + super->totalSwap *= this->pageSizeKb; + super->usedSwap *= this->pageSizeKb; +} + +static void DragonFlyBSDMachine_scanJails(DragonFlyBSDMachine* this) { + size_t len; + char* jails; /* Jail list */ + char* curpos; + char* nextpos; + + if (sysctlbyname("jail.list", NULL, &len, NULL, 0) == -1) { + CRT_fatalError("initial sysctlbyname / jail.list failed"); + } + +retry: + if (len == 0) + return; + + jails = xMalloc(len); + + if (sysctlbyname("jail.list", jails, &len, NULL, 0) == -1) { + if (errno == ENOMEM) { + free(jails); + goto retry; + } + CRT_fatalError("sysctlbyname / jail.list failed"); + } + + if (this->jails) { + Hashtable_delete(this->jails); + } + + this->jails = Hashtable_new(20, true); + curpos = jails; + while (curpos) { + int jailid; + char* str_hostname; + + nextpos = strchr(curpos, '\n'); + if (nextpos) { + *nextpos++ = 0; + } + + jailid = atoi(strtok(curpos, " ")); + str_hostname = strtok(NULL, " "); + + char* jname = (char*) (Hashtable_get(this->jails, jailid)); + if (jname == NULL) { + jname = xStrdup(str_hostname); + Hashtable_put(this->jails, jailid, jname); + } + + curpos = nextpos; + } + + free(jails); +} + +char* DragonFlyBSDMachine_readJailName(DragonFlyBSDMachine* host, int jailid) { + char* hostname; + char* jname; + + if (jailid != 0 && host->jails && (hostname = (char*)Hashtable_get(host->jails, jailid))) { + jname = xStrdup(hostname); + } else { + jname = xStrdup("-"); + } + + return jname; +} + +void Machine_scan(Machine* super) { + DragonFlyBSDMachine* this = (DragonFlyBSDMachine*) super; + + DragonFlyBSDMachine_scanMemoryInfo(super); + DragonFlyBSDMachine_scanCPUTime(super); + DragonFlyBSDMachine_scanJails(this); +} diff --git a/dragonflybsd/DragonFlyBSDMachine.h b/dragonflybsd/DragonFlyBSDMachine.h new file mode 100644 index 000000000..276a73d75 --- /dev/null +++ b/dragonflybsd/DragonFlyBSDMachine.h @@ -0,0 +1,61 @@ +#ifndef HEADER_DragonFlyBSDMachine +#define HEADER_DragonFlyBSDMachine +/* +htop - DragonFlyBSDMachine.h +(C) 2014 Hisham H. Muhammad +(C) 2017 Diederik de Groot +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include // required for kvm.h +#include +#include +#include +#include +#include +#include +#include + +#include "Hashtable.h" +#include "Machine.h" +#include "ProcessList.h" +#include "UsersTable.h" + + +typedef struct CPUData_ { + double userPercent; + double nicePercent; + double systemPercent; + double irqPercent; + double idlePercent; + double systemAllPercent; +} CPUData; + +typedef struct DragonFlyBSDMachine_ { + Machine super; + kvm_t* kd; + + Hashtable* jails; + + int pageSize; + int pageSizeKb; + int kernelFScale; + + unsigned long long int memWire; + unsigned long long int memActive; + unsigned long long int memInactive; + unsigned long long int memFree; + + CPUData* cpus; + + unsigned long* cp_time_o; + unsigned long* cp_time_n; + + unsigned long* cp_times_o; + unsigned long* cp_times_n; +} DragonFlyBSDMachine; + +char* DragonFlyBSDMachine_readJailName(DragonFlyBSDMachine* host, int jailid); + +#endif diff --git a/dragonflybsd/DragonFlyBSDProcessList.c b/dragonflybsd/DragonFlyBSDProcessList.c index c66917fb6..4ff17932c 100644 --- a/dragonflybsd/DragonFlyBSDProcessList.c +++ b/dragonflybsd/DragonFlyBSDProcessList.c @@ -22,248 +22,25 @@ in the source distribution for its full text. #include "CRT.h" #include "Macros.h" +#include "dragonflybsd/DragonFlyBSDMachine.h" #include "dragonflybsd/DragonFlyBSDProcess.h" -static int MIB_hw_physmem[2]; -static int MIB_vm_stats_vm_v_page_count[4]; -static int pageSize; -static int pageSizeKb; - -static int MIB_vm_stats_vm_v_wire_count[4]; -static int MIB_vm_stats_vm_v_active_count[4]; -static int MIB_vm_stats_vm_v_cache_count[4]; -static int MIB_vm_stats_vm_v_inactive_count[4]; -static int MIB_vm_stats_vm_v_free_count[4]; - -static int MIB_vfs_bufspace[2]; - -static int MIB_kern_cp_time[2]; -static int MIB_kern_cp_times[2]; -static int kernelFScale; - ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList) { - size_t len; - char errbuf[_POSIX2_LINE_MAX]; - DragonFlyBSDProcessList* dfpl = xCalloc(1, sizeof(DragonFlyBSDProcessList)); - ProcessList* pl = (ProcessList*) dfpl; - ProcessList_init(pl, Class(DragonFlyBSDProcess), host, pidMatchList); - - // physical memory in system: hw.physmem - // physical page size: hw.pagesize - // usable pagesize : vm.stats.vm.v_page_size - len = 2; sysctlnametomib("hw.physmem", MIB_hw_physmem, &len); - - len = sizeof(pageSize); - if (sysctlbyname("vm.stats.vm.v_page_size", &pageSize, &len, NULL, 0) == -1) - CRT_fatalError("Cannot get pagesize by sysctl"); - pageSizeKb = pageSize / ONE_K; - - // usable page count vm.stats.vm.v_page_count - // actually usable memory : vm.stats.vm.v_page_count * vm.stats.vm.v_page_size - len = 4; sysctlnametomib("vm.stats.vm.v_page_count", MIB_vm_stats_vm_v_page_count, &len); - - len = 4; sysctlnametomib("vm.stats.vm.v_wire_count", MIB_vm_stats_vm_v_wire_count, &len); - len = 4; sysctlnametomib("vm.stats.vm.v_active_count", MIB_vm_stats_vm_v_active_count, &len); - len = 4; sysctlnametomib("vm.stats.vm.v_cache_count", MIB_vm_stats_vm_v_cache_count, &len); - len = 4; sysctlnametomib("vm.stats.vm.v_inactive_count", MIB_vm_stats_vm_v_inactive_count, &len); - len = 4; sysctlnametomib("vm.stats.vm.v_free_count", MIB_vm_stats_vm_v_free_count, &len); - - len = 2; sysctlnametomib("vfs.bufspace", MIB_vfs_bufspace, &len); - - int cpus = 1; - len = sizeof(cpus); - if (sysctlbyname("hw.ncpu", &cpus, &len, NULL, 0) != 0) { - cpus = 1; - } + DragonFlyBSDProcessList* this = xCalloc(1, sizeof(DragonFlyBSDProcessList)); + ProcessList* super = (ProcessList*) this; - size_t sizeof_cp_time_array = sizeof(unsigned long) * CPUSTATES; - len = 2; sysctlnametomib("kern.cp_time", MIB_kern_cp_time, &len); - dfpl->cp_time_o = xCalloc(CPUSTATES, sizeof(unsigned long)); - dfpl->cp_time_n = xCalloc(CPUSTATES, sizeof(unsigned long)); - len = sizeof_cp_time_array; - - // fetch initial single (or average) CPU clicks from kernel - sysctl(MIB_kern_cp_time, 2, dfpl->cp_time_o, &len, NULL, 0); - - // on smp box, fetch rest of initial CPU's clicks - if (cpus > 1) { - len = 2; sysctlnametomib("kern.cp_times", MIB_kern_cp_times, &len); - dfpl->cp_times_o = xCalloc(cpus, sizeof_cp_time_array); - dfpl->cp_times_n = xCalloc(cpus, sizeof_cp_time_array); - len = cpus * sizeof_cp_time_array; - sysctl(MIB_kern_cp_times, 2, dfpl->cp_times_o, &len, NULL, 0); - } - - host->existingCPUs = MAXIMUM(cpus, 1); - // TODO: support offline CPUs and hot swapping - host->activeCPUs = host->existingCPUs; - - if (cpus == 1 ) { - dfpl->cpus = xRealloc(dfpl->cpus, sizeof(CPUData)); - } else { - // on smp we need CPUs + 1 to store averages too (as kernel kindly provides that as well) - dfpl->cpus = xRealloc(dfpl->cpus, (host->existingCPUs + 1) * sizeof(CPUData)); - } - - len = sizeof(kernelFScale); - if (sysctlbyname("kern.fscale", &kernelFScale, &len, NULL, 0) == -1) { - //sane default for kernel provided CPU percentage scaling, at least on x86 machines, in case this sysctl call failed - kernelFScale = 2048; - } + ProcessList_init(super, Class(DragonFlyBSDProcess), host, pidMatchList); - dfpl->kd = kvm_openfiles(NULL, "/dev/null", NULL, 0, errbuf); - if (dfpl->kd == NULL) { - CRT_fatalError("kvm_openfiles() failed"); - } - - return pl; + return super; } -void ProcessList_delete(ProcessList* this) { - const DragonFlyBSDProcessList* dfpl = (DragonFlyBSDProcessList*) this; - if (dfpl->kd) { - kvm_close(dfpl->kd); - } - - if (dfpl->jails) { - Hashtable_delete(dfpl->jails); - } - free(dfpl->cp_time_o); - free(dfpl->cp_time_n); - free(dfpl->cp_times_o); - free(dfpl->cp_times_n); - free(dfpl->cpus); - - ProcessList_done(this); +void ProcessList_delete(ProcessList* super) { + const DragonFlyBSDProcessList* this = (DragonFlyBSDProcessList*) super; + ProcessList_done(super); free(this); } -static inline void DragonFlyBSDProcessList_scanCPUTime(ProcessList* pl) { - const DragonFlyBSDProcessList* dfpl = (DragonFlyBSDProcessList*) pl; - - unsigned int cpus = pl->existingCPUs; // actual CPU count - unsigned int maxcpu = cpus; // max iteration (in case we have average + smp) - int cp_times_offset; - - assert(cpus > 0); - - size_t sizeof_cp_time_array; - - unsigned long* cp_time_n; // old clicks state - unsigned long* cp_time_o; // current clicks state - - unsigned long cp_time_d[CPUSTATES]; - double cp_time_p[CPUSTATES]; - - // get averages or single CPU clicks - sizeof_cp_time_array = sizeof(unsigned long) * CPUSTATES; - sysctl(MIB_kern_cp_time, 2, dfpl->cp_time_n, &sizeof_cp_time_array, NULL, 0); - - // get rest of CPUs - if (cpus > 1) { - // on smp systems DragonFlyBSD kernel concats all CPU states into one long array in - // kern.cp_times sysctl OID - // we store averages in dfpl->cpus[0], and actual cores after that - maxcpu = cpus + 1; - sizeof_cp_time_array = cpus * sizeof(unsigned long) * CPUSTATES; - sysctl(MIB_kern_cp_times, 2, dfpl->cp_times_n, &sizeof_cp_time_array, NULL, 0); - } - - for (unsigned int i = 0; i < maxcpu; i++) { - if (cpus == 1) { - // single CPU box - cp_time_n = dfpl->cp_time_n; - cp_time_o = dfpl->cp_time_o; - } else { - if (i == 0 ) { - // average - cp_time_n = dfpl->cp_time_n; - cp_time_o = dfpl->cp_time_o; - } else { - // specific smp cores - cp_times_offset = i - 1; - cp_time_n = dfpl->cp_times_n + (cp_times_offset * CPUSTATES); - cp_time_o = dfpl->cp_times_o + (cp_times_offset * CPUSTATES); - } - } - - // diff old vs new - unsigned long long total_o = 0; - unsigned long long total_n = 0; - unsigned long long total_d = 0; - for (int s = 0; s < CPUSTATES; s++) { - cp_time_d[s] = cp_time_n[s] - cp_time_o[s]; - total_o += cp_time_o[s]; - total_n += cp_time_n[s]; - } - - // totals - total_d = total_n - total_o; - if (total_d < 1 ) { - total_d = 1; - } - - // save current state as old and calc percentages - for (int s = 0; s < CPUSTATES; ++s) { - cp_time_o[s] = cp_time_n[s]; - cp_time_p[s] = ((double)cp_time_d[s]) / ((double)total_d) * 100; - } - - CPUData* cpuData = &(dfpl->cpus[i]); - cpuData->userPercent = cp_time_p[CP_USER]; - cpuData->nicePercent = cp_time_p[CP_NICE]; - cpuData->systemPercent = cp_time_p[CP_SYS]; - cpuData->irqPercent = cp_time_p[CP_INTR]; - cpuData->systemAllPercent = cp_time_p[CP_SYS] + cp_time_p[CP_INTR]; - // this one is not really used, but we store it anyway - cpuData->idlePercent = cp_time_p[CP_IDLE]; - } -} - -static inline void DragonFlyBSDProcessList_scanMemoryInfo(ProcessList* pl) { - DragonFlyBSDProcessList* dfpl = (DragonFlyBSDProcessList*) pl; - - // @etosan: - // memory counter relationships seem to be these: - // total = active + wired + inactive + cache + free - // htop_used (unavail to anybody) = active + wired - // htop_cache (for cache meter) = buffers + cache - // user_free (avail to procs) = buffers + inactive + cache + free - size_t len = sizeof(pl->totalMem); - - //disabled for now, as it is always smaller than phycal amount of memory... - //...to avoid "where is my memory?" questions - //sysctl(MIB_vm_stats_vm_v_page_count, 4, &(pl->totalMem), &len, NULL, 0); - //pl->totalMem *= pageSizeKb; - sysctl(MIB_hw_physmem, 2, &(pl->totalMem), &len, NULL, 0); - pl->totalMem /= 1024; - - sysctl(MIB_vm_stats_vm_v_active_count, 4, &(dfpl->memActive), &len, NULL, 0); - dfpl->memActive *= pageSizeKb; - - sysctl(MIB_vm_stats_vm_v_wire_count, 4, &(dfpl->memWire), &len, NULL, 0); - dfpl->memWire *= pageSizeKb; - - sysctl(MIB_vfs_bufspace, 2, &(pl->buffersMem), &len, NULL, 0); - pl->buffersMem /= 1024; - - sysctl(MIB_vm_stats_vm_v_cache_count, 4, &(pl->cachedMem), &len, NULL, 0); - pl->cachedMem *= pageSizeKb; - pl->usedMem = dfpl->memActive + dfpl->memWire; - - struct kvm_swap swap[16]; - int nswap = kvm_getswapinfo(dfpl->kd, swap, ARRAYSIZE(swap), 0); - pl->totalSwap = 0; - pl->usedSwap = 0; - for (int i = 0; i < nswap; i++) { - pl->totalSwap += swap[i].ksw_total; - pl->usedSwap += swap[i].ksw_used; - } - pl->totalSwap *= pageSizeKb; - pl->usedSwap *= pageSizeKb; -} - //static void DragonFlyBSDProcessList_updateExe(const struct kinfo_proc* kproc, Process* proc) { // const int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, kproc->kp_pid }; // char buffer[2048]; @@ -351,92 +128,17 @@ static void DragonFlyBSDProcessList_updateProcessName(kvm_t* kd, const struct ki free(cmdline); } -static inline void DragonFlyBSDProcessList_scanJails(DragonFlyBSDProcessList* dfpl) { - size_t len; - char* jls; /* Jail list */ - char* curpos; - char* nextpos; - - if (sysctlbyname("jail.list", NULL, &len, NULL, 0) == -1) { - CRT_fatalError("initial sysctlbyname / jail.list failed"); - } - -retry: - if (len == 0) - return; - - jls = xMalloc(len); - - if (sysctlbyname("jail.list", jls, &len, NULL, 0) == -1) { - if (errno == ENOMEM) { - free(jls); - goto retry; - } - CRT_fatalError("sysctlbyname / jail.list failed"); - } - - if (dfpl->jails) { - Hashtable_delete(dfpl->jails); - } - - dfpl->jails = Hashtable_new(20, true); - curpos = jls; - while (curpos) { - int jailid; - char* str_hostname; - - nextpos = strchr(curpos, '\n'); - if (nextpos) { - *nextpos++ = 0; - } - - jailid = atoi(strtok(curpos, " ")); - str_hostname = strtok(NULL, " "); - - char* jname = (char*) (Hashtable_get(dfpl->jails, jailid)); - if (jname == NULL) { - jname = xStrdup(str_hostname); - Hashtable_put(dfpl->jails, jailid, jname); - } - - curpos = nextpos; - } - - free(jls); -} - -static char* DragonFlyBSDProcessList_readJailName(DragonFlyBSDProcessList* dfpl, int jailid) { - char* hostname; - char* jname; - - if (jailid != 0 && dfpl->jails && (hostname = (char*)Hashtable_get(dfpl->jails, jailid))) { - jname = xStrdup(hostname); - } else { - jname = xStrdup("-"); - } - - return jname; -} - -void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { - DragonFlyBSDProcessList* dfpl = (DragonFlyBSDProcessList*) super; +void ProcessList_goThroughEntries(ProcessList* super) { const Machine* host = super->host; + const DragonFlyMachine* dhost = (const DragonFlyMachine*) host; const Settings* settings = host->settings; + bool hideKernelThreads = settings->hideKernelThreads; bool hideUserlandThreads = settings->hideUserlandThreads; - DragonFlyBSDProcessList_scanMemoryInfo(super); - DragonFlyBSDProcessList_scanCPUTime(super); - DragonFlyBSDProcessList_scanJails(dfpl); - - // in pause mode only gather global data for meters (CPU/memory/...) - if (pauseProcessUpdate) { - return; - } - int count = 0; - const struct kinfo_proc* kprocs = kvm_getprocs(dfpl->kd, KERN_PROC_ALL | (!hideUserlandThreads ? KERN_PROC_FLAG_LWP : 0), 0, &count); + const struct kinfo_proc* kprocs = kvm_getprocs(dhost->kd, KERN_PROC_ALL | (!hideUserlandThreads ? KERN_PROC_FLAG_LWP : 0), 0, &count); for (int i = 0; i < count; i++) { const struct kinfo_proc* kproc = &kprocs[i]; @@ -480,7 +182,7 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { } DragonFlyBSDProcessList_updateExe(kproc, proc); - DragonFlyBSDProcessList_updateProcessName(dfpl->kd, kproc, proc); + DragonFlyBSDProcessList_updateProcessName(dhost->kd, kproc, proc); if (settings->ss->flags & PROCESS_FLAG_CWD) { DragonFlyBSDProcessList_updateCwd(kproc, proc); @@ -488,13 +190,13 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { ProcessList_add(super, proc); - dfp->jname = DragonFlyBSDProcessList_readJailName(dfpl, kproc->kp_jailid); + dfp->jname = DragonFlyBSDMachine_readJailName(dhost, kproc->kp_jailid); } else { proc->processor = kproc->kp_lwp.kl_cpuid; if (dfp->jid != kproc->kp_jailid) { // process can enter jail anytime dfp->jid = kproc->kp_jailid; free(dfp->jname); - dfp->jname = DragonFlyBSDProcessList_readJailName(dfpl, kproc->kp_jailid); + dfp->jname = DragonFlyBSDMachine_readJailName(dhost, kproc->kp_jailid); } // if there are reapers in the system, process can get reparented anytime proc->ppid = kproc->kp_ppid; @@ -503,16 +205,16 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { proc->user = UsersTable_getRef(host->usersTable, proc->st_uid); } if (settings->updateProcessNames) { - DragonFlyBSDProcessList_updateProcessName(dfpl->kd, kproc, proc); + DragonFlyBSDProcessList_updateProcessName(dhost->kd, kproc, proc); } } proc->m_virt = kproc->kp_vm_map_size / ONE_K; - proc->m_resident = kproc->kp_vm_rssize * pageSizeKb; + proc->m_resident = kproc->kp_vm_rssize * dhost->pageSizeKb; proc->nlwp = kproc->kp_nthreads; // number of lwp thread proc->time = (kproc->kp_lwp.kl_uticks + kproc->kp_lwp.kl_sticks + kproc->kp_lwp.kl_iticks) / 10000; - proc->percent_cpu = 100.0 * ((double)kproc->kp_lwp.kl_pctcpu / (double)kernelFScale); + proc->percent_cpu = 100.0 * ((double)kproc->kp_lwp.kl_pctcpu / (double)dhost->kernelFScale); proc->percent_mem = 100.0 * proc->m_resident / (double)(super->totalMem); Process_updateCPUFieldWidths(proc->percent_cpu); @@ -605,22 +307,3 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { proc->updated = true; } } - -Machine* Machine_new(UsersTable* usersTable, uid_t userId) { - Machine* this = xCalloc(1, sizeof(Machine)); - Machine_init(this, usersTable, userId); - return this; -} - -void Machine_delete(Machine* host) { - free(host); -} - -bool Machine_isCPUonline(const Machine* host, unsigned int id) { - assert(id < host->existingCPUs); - - // TODO: support offline CPUs and hot swapping - (void) host; (void) id; - - return true; -} diff --git a/dragonflybsd/DragonFlyBSDProcessList.h b/dragonflybsd/DragonFlyBSDProcessList.h index 406722e03..3f5cdc3e2 100644 --- a/dragonflybsd/DragonFlyBSDProcessList.h +++ b/dragonflybsd/DragonFlyBSDProcessList.h @@ -8,61 +8,14 @@ Released under the GNU GPLv2+, see the COPYING file in the source distribution for its full text. */ -#include // required for kvm.h -#include -#include #include -#include #include -#include -#include -#include "Hashtable.h" #include "ProcessList.h" -#include "UsersTable.h" -#include "dragonflybsd/DragonFlyBSDProcess.h" - - -typedef struct CPUData_ { - double userPercent; - double nicePercent; - double systemPercent; - double irqPercent; - double idlePercent; - double systemAllPercent; -} CPUData; typedef struct DragonFlyBSDProcessList_ { ProcessList super; - kvm_t* kd; - - unsigned long long int memWire; - unsigned long long int memActive; - unsigned long long int memInactive; - unsigned long long int memFree; - - CPUData* cpus; - - unsigned long* cp_time_o; - unsigned long* cp_time_n; - - unsigned long* cp_times_o; - unsigned long* cp_times_n; - - Hashtable* jails; } DragonFlyBSDProcessList; -ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList); - -void ProcessList_delete(ProcessList* this); - -void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate); - -Machine* Machine_new(UsersTable* usersTable, uid_t userId); - -bool Machine_isCPUonline(const Machine* host, unsigned int id); - -void Machine_delete(Machine* host); - #endif diff --git a/dragonflybsd/Platform.c b/dragonflybsd/Platform.c index 1c0ef0a40..36307e931 100644 --- a/dragonflybsd/Platform.c +++ b/dragonflybsd/Platform.c @@ -173,15 +173,15 @@ int Platform_getMaxPid(void) { double Platform_setCPUValues(Meter* this, unsigned int cpu) { const Machine* host = this->host; - const DragonFlyBSDProcessList* fpl = (const DragonFlyBSDProcessList*) host->pl; - unsigned int cpus = this->host->activeCPUs; + const DragonFlyBSDMachine* dhost = (const DragonFlyBSDMachine*) host; + unsigned int cpus = host->activeCPUs; const CPUData* cpuData; if (cpus == 1) { // single CPU box has everything in fpl->cpus[0] - cpuData = &(fpl->cpus[0]); + cpuData = &(dhost->cpus[0]); } else { - cpuData = &(fpl->cpus[cpu]); + cpuData = &(dhost->cpus[cpu]); } double percent; @@ -189,7 +189,7 @@ double Platform_setCPUValues(Meter* this, unsigned int cpu) { v[CPU_METER_NICE] = cpuData->nicePercent; v[CPU_METER_NORMAL] = cpuData->userPercent; - if (host->settings->detailedCPUTime) { + if (super->settings->detailedCPUTime) { v[CPU_METER_KERNEL] = cpuData->systemPercent; v[CPU_METER_IRQ] = cpuData->irqPercent; this->curItems = 4; @@ -209,22 +209,21 @@ double Platform_setCPUValues(Meter* this, unsigned int cpu) { } void Platform_setMemoryValues(Meter* this) { - // TODO - const ProcessList* pl = this->host->pl; + const Machine* host = this->host; - this->total = pl->totalMem; - this->values[MEMORY_METER_USED] = pl->usedMem; - this->values[MEMORY_METER_BUFFERS] = pl->buffersMem; + this->total = host->totalMem; + this->values[MEMORY_METER_USED] = host->usedMem; + this->values[MEMORY_METER_BUFFERS] = host->buffersMem; // this->values[MEMORY_METER_SHARED] = "shared memory, like tmpfs and shm" // mtr->values[MEMORY_METER_COMPRESSED] = "compressed memory, like zswap on linux" - this->values[MEMORY_METER_CACHE] = pl->cachedMem; + this->values[MEMORY_METER_CACHE] = host->cachedMem; // this->values[MEMORY_METER_AVAILABLE] = "available memory" } void Platform_setSwapValues(Meter* this) { - const ProcessList* pl = this->host->pl; - this->total = pl->totalSwap; - this->values[SWAP_METER_USED] = pl->usedSwap; + const Machine* host = this->host; + this->total = host->totalSwap; + this->values[SWAP_METER_USED] = host->usedSwap; // mtr->values[SWAP_METER_CACHE] = "pages that are both in swap and RAM, like SwapCached on linux" // mtr->values[SWAP_METER_FRONTSWAP] = "pages that are accounted to swap but stored elsewhere, like frontswap on linux" } diff --git a/freebsd/FreeBSDMachine.c b/freebsd/FreeBSDMachine.c new file mode 100644 index 000000000..b8d5d87fb --- /dev/null +++ b/freebsd/FreeBSDMachine.c @@ -0,0 +1,396 @@ +/* +htop - FreeBSDMachine.c +(C) 2014 Hisham H. Muhammad +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "config.h" // IWYU pragma: keep + +#include "freebsd/FreeBSDMachine.h" + +#include +#include +#include +#include +#include +#include +#include +#include // needs to be included before for MAXPATHLEN +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "CRT.h" +#include "Compat.h" +#include "Macros.h" +#include "Object.h" +#include "Scheduling.h" +#include "Settings.h" +#include "XUtils.h" +#include "generic/openzfs_sysctl.h" +#include "zfs/ZfsArcStats.h" + + +static int MIB_hw_physmem[2]; +static int MIB_vm_stats_vm_v_page_count[4]; + +static int MIB_vm_stats_vm_v_wire_count[4]; +static int MIB_vm_stats_vm_v_active_count[4]; +static int MIB_vm_stats_vm_v_cache_count[4]; +static int MIB_vm_stats_vm_v_inactive_count[4]; +static int MIB_vm_stats_vm_v_free_count[4]; +static int MIB_vm_vmtotal[2]; + +static int MIB_vfs_bufspace[2]; + +static int MIB_kern_cp_time[2]; +static int MIB_kern_cp_times[2]; + +Machine* Machine_new(UsersTable* usersTable, uid_t userId) { + FreeBSDMachine* this = xCalloc(1, sizeof(FreeBSDMachine)); + Machine* super = &this->super; + char errbuf[_POSIX2_LINE_MAX]; + size_t len; + + Machine_init(this, usersTable, userId); + + // physical memory in system: hw.physmem + // physical page size: hw.pagesize + // usable pagesize : vm.stats.vm.v_page_size + len = 2; sysctlnametomib("hw.physmem", MIB_hw_physmem, &len); + + len = sizeof(this->pageSize); + if (sysctlbyname("vm.stats.vm.v_page_size", &this->pageSize, &len, NULL, 0) == -1) + CRT_fatalError("Cannot get pagesize by sysctl"); + this->pageSizeKb = this->pageSize / ONE_K; + + // usable page count vm.stats.vm.v_page_count + // actually usable memory : vm.stats.vm.v_page_count * vm.stats.vm.v_page_size + len = 4; sysctlnametomib("vm.stats.vm.v_page_count", MIB_vm_stats_vm_v_page_count, &len); + + len = 4; sysctlnametomib("vm.stats.vm.v_wire_count", MIB_vm_stats_vm_v_wire_count, &len); + len = 4; sysctlnametomib("vm.stats.vm.v_active_count", MIB_vm_stats_vm_v_active_count, &len); + len = 4; sysctlnametomib("vm.stats.vm.v_cache_count", MIB_vm_stats_vm_v_cache_count, &len); + len = 4; sysctlnametomib("vm.stats.vm.v_inactive_count", MIB_vm_stats_vm_v_inactive_count, &len); + len = 4; sysctlnametomib("vm.stats.vm.v_free_count", MIB_vm_stats_vm_v_free_count, &len); + len = 2; sysctlnametomib("vm.vmtotal", MIB_vm_vmtotal, &len); + + len = 2; sysctlnametomib("vfs.bufspace", MIB_vfs_bufspace, &len); + + openzfs_sysctl_init(&this->zfs); + openzfs_sysctl_updateArcStats(&this->zfs); + + int smp = 0; + len = sizeof(smp); + + if (sysctlbyname("kern.smp.active", &smp, &len, NULL, 0) != 0 || len != sizeof(smp)) { + smp = 0; + } + + int cpus = 1; + len = sizeof(cpus); + + if (smp) { + int err = sysctlbyname("kern.smp.cpus", &cpus, &len, NULL, 0); + if (err) { + cpus = 1; + } + } else { + cpus = 1; + } + + size_t sizeof_cp_time_array = sizeof(unsigned long) * CPUSTATES; + len = 2; sysctlnametomib("kern.cp_time", MIB_kern_cp_time, &len); + this->cp_time_o = xCalloc(CPUSTATES, sizeof(unsigned long)); + this->cp_time_n = xCalloc(CPUSTATES, sizeof(unsigned long)); + len = sizeof_cp_time_array; + + // fetch initial single (or average) CPU clicks from kernel + sysctl(MIB_kern_cp_time, 2, this->cp_time_o, &len, NULL, 0); + + // on smp box, fetch rest of initial CPU's clicks + if (cpus > 1) { + len = 2; sysctlnametomib("kern.cp_times", MIB_kern_cp_times, &len); + this->cp_times_o = xCalloc(cpus, sizeof_cp_time_array); + this->cp_times_n = xCalloc(cpus, sizeof_cp_time_array); + len = cpus * sizeof_cp_time_array; + sysctl(MIB_kern_cp_times, 2, this->cp_times_o, &len, NULL, 0); + } + + super->existingCPUs = MAXIMUM(cpus, 1); + // TODO: support offline CPUs and hot swapping + super->activeCPUs = super->existingCPUs; + + if (cpus == 1 ) { + this->cpus = xRealloc(this->cpus, sizeof(CPUData)); + } else { + // on smp we need CPUs + 1 to store averages too (as kernel kindly provides that as well) + this->cpus = xRealloc(this->cpus, (super->existingCPUs + 1) * sizeof(CPUData)); + } + + len = sizeof(this->kernelFScale); + if (sysctlbyname("kern.fscale", &this->kernelFScale, &len, NULL, 0) == -1) { + //sane default for kernel provided CPU percentage scaling, at least on x86 machines, in case this sysctl call failed + this->kernelFScale = 2048; + } + + this->kd = kvm_openfiles(NULL, "/dev/null", NULL, 0, errbuf); + if (this->kd == NULL) { + CRT_fatalError("kvm_openfiles() failed"); + } + + return this; +} + +void Machine_delete(Machine* super) { + FreeBSDMachine* this = (FreeBSDMachine*) super; + + ProcessList_done(this); + + if (this->kd) { + kvm_close(this->kd); + } + + free(this->cp_time_o); + free(this->cp_time_n); + free(this->cp_times_o); + free(this->cp_times_n); + free(this->cpus); + + free(this); +} + +static inline void FreeBSDMachine_scanCPU(Machine* super) { + const FreeBSDMachine* this = (FreeBSDMachine*) super; + + unsigned int cpus = super->existingCPUs; // actual CPU count + unsigned int maxcpu = cpus; // max iteration (in case we have average + smp) + int cp_times_offset; + + assert(cpus > 0); + + size_t sizeof_cp_time_array; + + unsigned long* cp_time_n; // old clicks state + unsigned long* cp_time_o; // current clicks state + + unsigned long cp_time_d[CPUSTATES]; + double cp_time_p[CPUSTATES]; + + // get averages or single CPU clicks + sizeof_cp_time_array = sizeof(unsigned long) * CPUSTATES; + sysctl(MIB_kern_cp_time, 2, this->cp_time_n, &sizeof_cp_time_array, NULL, 0); + + // get rest of CPUs + if (cpus > 1) { + // on smp systems FreeBSD kernel concats all CPU states into one long array in + // kern.cp_times sysctl OID + // we store averages in this->cpus[0], and actual cores after that + maxcpu = cpus + 1; + sizeof_cp_time_array = cpus * sizeof(unsigned long) * CPUSTATES; + sysctl(MIB_kern_cp_times, 2, this->cp_times_n, &sizeof_cp_time_array, NULL, 0); + } + + for (unsigned int i = 0; i < maxcpu; i++) { + if (cpus == 1) { + // single CPU box + cp_time_n = this->cp_time_n; + cp_time_o = this->cp_time_o; + } else { + if (i == 0 ) { + // average + cp_time_n = this->cp_time_n; + cp_time_o = this->cp_time_o; + } else { + // specific smp cores + cp_times_offset = i - 1; + cp_time_n = this->cp_times_n + (cp_times_offset * CPUSTATES); + cp_time_o = this->cp_times_o + (cp_times_offset * CPUSTATES); + } + } + + // diff old vs new + unsigned long long total_o = 0; + unsigned long long total_n = 0; + unsigned long long total_d = 0; + for (int s = 0; s < CPUSTATES; s++) { + cp_time_d[s] = cp_time_n[s] - cp_time_o[s]; + total_o += cp_time_o[s]; + total_n += cp_time_n[s]; + } + + // totals + total_d = total_n - total_o; + if (total_d < 1 ) { + total_d = 1; + } + + // save current state as old and calc percentages + for (int s = 0; s < CPUSTATES; ++s) { + cp_time_o[s] = cp_time_n[s]; + cp_time_p[s] = ((double)cp_time_d[s]) / ((double)total_d) * 100; + } + + CPUData* cpuData = &(this->cpus[i]); + cpuData->userPercent = cp_time_p[CP_USER]; + cpuData->nicePercent = cp_time_p[CP_NICE]; + cpuData->systemPercent = cp_time_p[CP_SYS]; + cpuData->irqPercent = cp_time_p[CP_INTR]; + cpuData->systemAllPercent = cp_time_p[CP_SYS] + cp_time_p[CP_INTR]; + // this one is not really used + //cpuData->idlePercent = cp_time_p[CP_IDLE]; + + cpuData->temperature = NAN; + cpuData->frequency = NAN; + + const int coreId = (cpus == 1) ? 0 : ((int)i - 1); + if (coreId < 0) + continue; + + // TODO: test with hyperthreading and multi-cpu systems + if (super->settings->showCPUTemperature) { + int temperature; + size_t len = sizeof(temperature); + char mibBuffer[32]; + xSnprintf(mibBuffer, sizeof(mibBuffer), "dev.cpu.%d.temperature", coreId); + int r = sysctlbyname(mibBuffer, &temperature, &len, NULL, 0); + if (r == 0) + cpuData->temperature = (double)(temperature - 2732) / 10.0; // convert from deci-Kelvin to Celsius + } + + // TODO: test with hyperthreading and multi-cpu systems + if (super->settings->showCPUFrequency) { + int frequency; + size_t len = sizeof(frequency); + char mibBuffer[32]; + xSnprintf(mibBuffer, sizeof(mibBuffer), "dev.cpu.%d.freq", coreId); + int r = sysctlbyname(mibBuffer, &frequency, &len, NULL, 0); + if (r == 0) + cpuData->frequency = frequency; // keep in MHz + } + } + + // calculate max temperature and avg frequency for average meter and + // propagate frequency to all cores if only supplied for CPU 0 + if (cpus > 1) { + if (super->settings->showCPUTemperature) { + double maxTemp = NAN; + for (unsigned int i = 1; i < maxcpu; i++) { + const double coreTemp = this->cpus[i].temperature; + if (isnan(coreTemp)) + continue; + + maxTemp = MAXIMUM(maxTemp, coreTemp); + } + + this->cpus[0].temperature = maxTemp; + } + + if (super->settings->showCPUFrequency) { + const double coreZeroFreq = this->cpus[1].frequency; + double freqSum = coreZeroFreq; + if (!isnan(coreZeroFreq)) { + for (unsigned int i = 2; i < maxcpu; i++) { + if (isnan(this->cpus[i].frequency)) + this->cpus[i].frequency = coreZeroFreq; + + freqSum += this->cpus[i].frequency; + } + + this->cpus[0].frequency = freqSum / (maxcpu - 1); + } + } + } +} + +static void FreeBSDMachine_scanMemoryInfo(Machine* super) { + FreeBSDMachine* this = (FreeBSDMachine*) super; + + // @etosan: + // memory counter relationships seem to be these: + // total = active + wired + inactive + cache + free + // htop_used (unavail to anybody) = active + wired + // htop_cache (for cache meter) = buffers + cache + // user_free (avail to procs) = buffers + inactive + cache + free + // + // with ZFS ARC situation becomes bit muddled, as ARC behaves like "user_free" + // and belongs into cache, but is reported as wired by kernel + // + // htop_used = active + (wired - arc) + // htop_cache = buffers + cache + arc + u_long totalMem; + u_int memActive, memWire, cachedMem; + long buffersMem; + size_t len; + struct vmtotal vmtotal; + + //disabled for now, as it is always smaller than phycal amount of memory... + //...to avoid "where is my memory?" questions + //sysctl(MIB_vm_stats_vm_v_page_count, 4, &(super->totalMem), &len, NULL, 0); + //super->totalMem *= this->pageSizeKb; + len = sizeof(totalMem); + sysctl(MIB_hw_physmem, 2, &(totalMem), &len, NULL, 0); + totalMem /= 1024; + super->totalMem = totalMem; + + len = sizeof(memActive); + sysctl(MIB_vm_stats_vm_v_active_count, 4, &(memActive), &len, NULL, 0); + memActive *= this->pageSizeKb; + this->memActive = memActive; + + len = sizeof(memWire); + sysctl(MIB_vm_stats_vm_v_wire_count, 4, &(memWire), &len, NULL, 0); + memWire *= this->pageSizeKb; + this->memWire = memWire; + + len = sizeof(buffersMem); + sysctl(MIB_vfs_bufspace, 2, &(buffersMem), &len, NULL, 0); + buffersMem /= 1024; + super->buffersMem = buffersMem; + + len = sizeof(cachedMem); + sysctl(MIB_vm_stats_vm_v_cache_count, 4, &(cachedMem), &len, NULL, 0); + cachedMem *= this->pageSizeKb; + super->cachedMem = cachedMem; + + len = sizeof(vmtotal); + sysctl(MIB_vm_vmtotal, 2, &(vmtotal), &len, NULL, 0); + super->sharedMem = vmtotal.t_rmshr * this->pageSizeKb; + + super->usedMem = this->memActive + this->memWire; + + struct kvm_swap swap[16]; + int nswap = kvm_getswapinfo(this->kd, swap, ARRAYSIZE(swap), 0); + super->totalSwap = 0; + super->usedSwap = 0; + for (int i = 0; i < nswap; i++) { + super->totalSwap += swap[i].ksw_total; + super->usedSwap += swap[i].ksw_used; + } + super->totalSwap *= this->pageSizeKb; + super->usedSwap *= this->pageSizeKb; +} + +void Machine_scan(Machine* super) { + FreeBSDMachine* this = (FreeBSDMachine*) super; + + openzfs_sysctl_updateArcStats(&this->zfs); + FreeBSDMachine_scanMemoryInfo(super); + FreeBSDMachine_scanCPU(super); +} + +bool Machine_isCPUonline(const Machine* host, unsigned int id) { + assert(id < host->existingCPUs); + + // TODO: support offline CPUs and hot swapping + (void) host; (void) id; + + return true; +} diff --git a/freebsd/FreeBSDMachine.h b/freebsd/FreeBSDMachine.h new file mode 100644 index 000000000..1a90534ee --- /dev/null +++ b/freebsd/FreeBSDMachine.h @@ -0,0 +1,54 @@ +#ifndef HEADER_FreeBSDMachine +#define HEADER_FreeBSDMachine +/* +htop - FreeBSDMachine.h +(C) 2014 Hisham H. Muhammad +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include +#include +#include + +#include "Hashtable.h" +#include "Machine.h" +#include "UsersTable.h" +#include "zfs/ZfsArcStats.h" + + +typedef struct CPUData_ { + double userPercent; + double nicePercent; + double systemPercent; + double irqPercent; + double systemAllPercent; + + double frequency; + double temperature; +} CPUData; + +typedef struct FreeBSDProcessList_ { + Machine super; + kvm_t* kd; + + int pageSize; + int pageSizeKb; + int kernelFScale; + + unsigned long long int memWire; + unsigned long long int memActive; + + ZfsArcStats zfs; + + CPUData* cpus; + + unsigned long* cp_time_o; + unsigned long* cp_time_n; + + unsigned long* cp_times_o; + unsigned long* cp_times_n; + +} FreeBSDProcessList; + +#endif diff --git a/freebsd/FreeBSDProcessList.c b/freebsd/FreeBSDProcessList.c index bd6e3aacd..1cf325028 100644 --- a/freebsd/FreeBSDProcessList.c +++ b/freebsd/FreeBSDProcessList.c @@ -37,354 +37,25 @@ in the source distribution for its full text. #include "Scheduling.h" #include "Settings.h" #include "XUtils.h" -#include "generic/openzfs_sysctl.h" -#include "zfs/ZfsArcStats.h" -static int MIB_hw_physmem[2]; -static int MIB_vm_stats_vm_v_page_count[4]; -static int pageSize; -static int pageSizeKb; - -static int MIB_vm_stats_vm_v_wire_count[4]; -static int MIB_vm_stats_vm_v_active_count[4]; -static int MIB_vm_stats_vm_v_cache_count[4]; -static int MIB_vm_stats_vm_v_inactive_count[4]; -static int MIB_vm_stats_vm_v_free_count[4]; -static int MIB_vm_vmtotal[2]; - -static int MIB_vfs_bufspace[2]; - -static int MIB_kern_cp_time[2]; -static int MIB_kern_cp_times[2]; -static int kernelFScale; - ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList) { - size_t len; - char errbuf[_POSIX2_LINE_MAX]; - FreeBSDProcessList* fpl = xCalloc(1, sizeof(FreeBSDProcessList)); - ProcessList* pl = (ProcessList*) fpl; - ProcessList_init(pl, Class(FreeBSDProcess), host, pidMatchList); - - // physical memory in system: hw.physmem - // physical page size: hw.pagesize - // usable pagesize : vm.stats.vm.v_page_size - len = 2; sysctlnametomib("hw.physmem", MIB_hw_physmem, &len); - - len = sizeof(pageSize); - if (sysctlbyname("vm.stats.vm.v_page_size", &pageSize, &len, NULL, 0) == -1) - CRT_fatalError("Cannot get pagesize by sysctl"); - pageSizeKb = pageSize / ONE_K; - - // usable page count vm.stats.vm.v_page_count - // actually usable memory : vm.stats.vm.v_page_count * vm.stats.vm.v_page_size - len = 4; sysctlnametomib("vm.stats.vm.v_page_count", MIB_vm_stats_vm_v_page_count, &len); - - len = 4; sysctlnametomib("vm.stats.vm.v_wire_count", MIB_vm_stats_vm_v_wire_count, &len); - len = 4; sysctlnametomib("vm.stats.vm.v_active_count", MIB_vm_stats_vm_v_active_count, &len); - len = 4; sysctlnametomib("vm.stats.vm.v_cache_count", MIB_vm_stats_vm_v_cache_count, &len); - len = 4; sysctlnametomib("vm.stats.vm.v_inactive_count", MIB_vm_stats_vm_v_inactive_count, &len); - len = 4; sysctlnametomib("vm.stats.vm.v_free_count", MIB_vm_stats_vm_v_free_count, &len); - len = 2; sysctlnametomib("vm.vmtotal", MIB_vm_vmtotal, &len); - - len = 2; sysctlnametomib("vfs.bufspace", MIB_vfs_bufspace, &len); - - openzfs_sysctl_init(&fpl->zfs); - openzfs_sysctl_updateArcStats(&fpl->zfs); - - int smp = 0; - len = sizeof(smp); - - if (sysctlbyname("kern.smp.active", &smp, &len, NULL, 0) != 0 || len != sizeof(smp)) { - smp = 0; - } + FreeBSDProcessList* this = xCalloc(1, sizeof(FreeBSDProcessList)); + ProcessList* super = &this->super; - int cpus = 1; - len = sizeof(cpus); - - if (smp) { - int err = sysctlbyname("kern.smp.cpus", &cpus, &len, NULL, 0); - if (err) { - cpus = 1; - } - } else { - cpus = 1; - } - - size_t sizeof_cp_time_array = sizeof(unsigned long) * CPUSTATES; - len = 2; sysctlnametomib("kern.cp_time", MIB_kern_cp_time, &len); - fpl->cp_time_o = xCalloc(CPUSTATES, sizeof(unsigned long)); - fpl->cp_time_n = xCalloc(CPUSTATES, sizeof(unsigned long)); - len = sizeof_cp_time_array; - - // fetch initial single (or average) CPU clicks from kernel - sysctl(MIB_kern_cp_time, 2, fpl->cp_time_o, &len, NULL, 0); - - // on smp box, fetch rest of initial CPU's clicks - if (cpus > 1) { - len = 2; sysctlnametomib("kern.cp_times", MIB_kern_cp_times, &len); - fpl->cp_times_o = xCalloc(cpus, sizeof_cp_time_array); - fpl->cp_times_n = xCalloc(cpus, sizeof_cp_time_array); - len = cpus * sizeof_cp_time_array; - sysctl(MIB_kern_cp_times, 2, fpl->cp_times_o, &len, NULL, 0); - } + ProcessList_init(super, Class(FreeBSDProcess), host, pidMatchList); - host->existingCPUs = MAXIMUM(cpus, 1); - // TODO: support offline CPUs and hot swapping - host->activeCPUs = host->existingCPUs; - - if (cpus == 1 ) { - fpl->cpus = xRealloc(fpl->cpus, sizeof(CPUData)); - } else { - // on smp we need CPUs + 1 to store averages too (as kernel kindly provides that as well) - fpl->cpus = xRealloc(fpl->cpus, (host->existingCPUs + 1) * sizeof(CPUData)); - } - - - len = sizeof(kernelFScale); - if (sysctlbyname("kern.fscale", &kernelFScale, &len, NULL, 0) == -1) { - //sane default for kernel provided CPU percentage scaling, at least on x86 machines, in case this sysctl call failed - kernelFScale = 2048; - } - - fpl->kd = kvm_openfiles(NULL, "/dev/null", NULL, 0, errbuf); - if (fpl->kd == NULL) { - CRT_fatalError("kvm_openfiles() failed"); - } - - return pl; + return this; } -void ProcessList_delete(ProcessList* this) { - const FreeBSDProcessList* fpl = (FreeBSDProcessList*) this; +void ProcessList_delete(ProcessList* super) { + FreeBSDProcessList* this = (FreeBSDProcessList*) super; - if (fpl->kd) { - kvm_close(fpl->kd); - } - - free(fpl->cp_time_o); - free(fpl->cp_time_n); - free(fpl->cp_times_o); - free(fpl->cp_times_n); - free(fpl->cpus); + ProcessList_done(super); - ProcessList_done(this); free(this); } -static inline void FreeBSDProcessList_scanCPU(ProcessList* pl) { - const FreeBSDProcessList* fpl = (FreeBSDProcessList*) pl; - const Machine* host = pl->host; - - unsigned int cpus = host->existingCPUs; // actual CPU count - unsigned int maxcpu = cpus; // max iteration (in case we have average + smp) - int cp_times_offset; - - assert(cpus > 0); - - size_t sizeof_cp_time_array; - - unsigned long* cp_time_n; // old clicks state - unsigned long* cp_time_o; // current clicks state - - unsigned long cp_time_d[CPUSTATES]; - double cp_time_p[CPUSTATES]; - - // get averages or single CPU clicks - sizeof_cp_time_array = sizeof(unsigned long) * CPUSTATES; - sysctl(MIB_kern_cp_time, 2, fpl->cp_time_n, &sizeof_cp_time_array, NULL, 0); - - // get rest of CPUs - if (cpus > 1) { - // on smp systems FreeBSD kernel concats all CPU states into one long array in - // kern.cp_times sysctl OID - // we store averages in fpl->cpus[0], and actual cores after that - maxcpu = cpus + 1; - sizeof_cp_time_array = cpus * sizeof(unsigned long) * CPUSTATES; - sysctl(MIB_kern_cp_times, 2, fpl->cp_times_n, &sizeof_cp_time_array, NULL, 0); - } - - for (unsigned int i = 0; i < maxcpu; i++) { - if (cpus == 1) { - // single CPU box - cp_time_n = fpl->cp_time_n; - cp_time_o = fpl->cp_time_o; - } else { - if (i == 0 ) { - // average - cp_time_n = fpl->cp_time_n; - cp_time_o = fpl->cp_time_o; - } else { - // specific smp cores - cp_times_offset = i - 1; - cp_time_n = fpl->cp_times_n + (cp_times_offset * CPUSTATES); - cp_time_o = fpl->cp_times_o + (cp_times_offset * CPUSTATES); - } - } - - // diff old vs new - unsigned long long total_o = 0; - unsigned long long total_n = 0; - unsigned long long total_d = 0; - for (int s = 0; s < CPUSTATES; s++) { - cp_time_d[s] = cp_time_n[s] - cp_time_o[s]; - total_o += cp_time_o[s]; - total_n += cp_time_n[s]; - } - - // totals - total_d = total_n - total_o; - if (total_d < 1 ) { - total_d = 1; - } - - // save current state as old and calc percentages - for (int s = 0; s < CPUSTATES; ++s) { - cp_time_o[s] = cp_time_n[s]; - cp_time_p[s] = ((double)cp_time_d[s]) / ((double)total_d) * 100; - } - - CPUData* cpuData = &(fpl->cpus[i]); - cpuData->userPercent = cp_time_p[CP_USER]; - cpuData->nicePercent = cp_time_p[CP_NICE]; - cpuData->systemPercent = cp_time_p[CP_SYS]; - cpuData->irqPercent = cp_time_p[CP_INTR]; - cpuData->systemAllPercent = cp_time_p[CP_SYS] + cp_time_p[CP_INTR]; - // this one is not really used - //cpuData->idlePercent = cp_time_p[CP_IDLE]; - - cpuData->temperature = NAN; - cpuData->frequency = NAN; - - const int coreId = (cpus == 1) ? 0 : ((int)i - 1); - if (coreId < 0) - continue; - - // TODO: test with hyperthreading and multi-cpu systems - if (host->settings->showCPUTemperature) { - int temperature; - size_t len = sizeof(temperature); - char mibBuffer[32]; - xSnprintf(mibBuffer, sizeof(mibBuffer), "dev.cpu.%d.temperature", coreId); - int r = sysctlbyname(mibBuffer, &temperature, &len, NULL, 0); - if (r == 0) - cpuData->temperature = (double)(temperature - 2732) / 10.0; // convert from deci-Kelvin to Celsius - } - - // TODO: test with hyperthreading and multi-cpu systems - if (host->settings->showCPUFrequency) { - int frequency; - size_t len = sizeof(frequency); - char mibBuffer[32]; - xSnprintf(mibBuffer, sizeof(mibBuffer), "dev.cpu.%d.freq", coreId); - int r = sysctlbyname(mibBuffer, &frequency, &len, NULL, 0); - if (r == 0) - cpuData->frequency = frequency; // keep in MHz - } - } - - // calculate max temperature and avg frequency for average meter and - // propagate frequency to all cores if only supplied for CPU 0 - if (cpus > 1) { - if (host->settings->showCPUTemperature) { - double maxTemp = NAN; - for (unsigned int i = 1; i < maxcpu; i++) { - const double coreTemp = fpl->cpus[i].temperature; - if (isnan(coreTemp)) - continue; - - maxTemp = MAXIMUM(maxTemp, coreTemp); - } - - fpl->cpus[0].temperature = maxTemp; - } - - if (host->settings->showCPUFrequency) { - const double coreZeroFreq = fpl->cpus[1].frequency; - double freqSum = coreZeroFreq; - if (!isnan(coreZeroFreq)) { - for (unsigned int i = 2; i < maxcpu; i++) { - if (isnan(fpl->cpus[i].frequency)) - fpl->cpus[i].frequency = coreZeroFreq; - - freqSum += fpl->cpus[i].frequency; - } - - fpl->cpus[0].frequency = freqSum / (maxcpu - 1); - } - } - } -} - -static inline void FreeBSDProcessList_scanMemoryInfo(ProcessList* pl) { - FreeBSDProcessList* fpl = (FreeBSDProcessList*) pl; - Machine* host = pl->host; - - // @etosan: - // memory counter relationships seem to be these: - // total = active + wired + inactive + cache + free - // htop_used (unavail to anybody) = active + wired - // htop_cache (for cache meter) = buffers + cache - // user_free (avail to procs) = buffers + inactive + cache + free - // - // with ZFS ARC situation becomes bit muddled, as ARC behaves like "user_free" - // and belongs into cache, but is reported as wired by kernel - // - // htop_used = active + (wired - arc) - // htop_cache = buffers + cache + arc - u_long totalMem; - u_int memActive, memWire, cachedMem; - long buffersMem; - size_t len; - struct vmtotal vmtotal; - - //disabled for now, as it is always smaller than phycal amount of memory... - //...to avoid "where is my memory?" questions - //sysctl(MIB_vm_stats_vm_v_page_count, 4, &(host->totalMem), &len, NULL, 0); - //host->totalMem *= pageSizeKb; - len = sizeof(totalMem); - sysctl(MIB_hw_physmem, 2, &(totalMem), &len, NULL, 0); - totalMem /= 1024; - host->totalMem = totalMem; - - len = sizeof(memActive); - sysctl(MIB_vm_stats_vm_v_active_count, 4, &(memActive), &len, NULL, 0); - memActive *= pageSizeKb; - fpl->memActive = memActive; - - len = sizeof(memWire); - sysctl(MIB_vm_stats_vm_v_wire_count, 4, &(memWire), &len, NULL, 0); - memWire *= pageSizeKb; - fpl->memWire = memWire; - - len = sizeof(buffersMem); - sysctl(MIB_vfs_bufspace, 2, &(buffersMem), &len, NULL, 0); - buffersMem /= 1024; - host->buffersMem = buffersMem; - - len = sizeof(cachedMem); - sysctl(MIB_vm_stats_vm_v_cache_count, 4, &(cachedMem), &len, NULL, 0); - cachedMem *= pageSizeKb; - host->cachedMem = cachedMem; - - len = sizeof(vmtotal); - sysctl(MIB_vm_vmtotal, 2, &(vmtotal), &len, NULL, 0); - host->sharedMem = vmtotal.t_rmshr * pageSizeKb; - - host->usedMem = fpl->memActive + fpl->memWire; - - struct kvm_swap swap[16]; - int nswap = kvm_getswapinfo(fpl->kd, swap, ARRAYSIZE(swap), 0); - host->totalSwap = 0; - host->usedSwap = 0; - for (int i = 0; i < nswap; i++) { - host->totalSwap += swap[i].ksw_total; - host->usedSwap += swap[i].ksw_used; - } - host->totalSwap *= pageSizeKb; - host->usedSwap *= pageSizeKb; -} - static void FreeBSDProcessList_updateExe(const struct kinfo_proc* kproc, Process* proc) { if (Process_isKernelThread(proc)) { Process_updateExe(proc, NULL); @@ -483,22 +154,14 @@ IGNORE_WCASTQUAL_END return NULL; } -void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { - FreeBSDProcessList* fpl = (FreeBSDProcessList*) super; +void ProcessList_goThroughEntries(ProcessList* super) { const Machine* host = super->host; + const FreeBSDMachine* fhost = (FreeBSDMachine*) host; + FreeBSDProcessList* fpl = (FreeBSDProcessList*) super; const Settings* settings = host->settings; bool hideKernelThreads = settings->hideKernelThreads; bool hideUserlandThreads = settings->hideUserlandThreads; - openzfs_sysctl_updateArcStats(&fpl->zfs); - FreeBSDProcessList_scanMemoryInfo(super); - FreeBSDProcessList_scanCPU(super); - - // in pause mode only gather global data for meters (CPU/memory/...) - if (pauseProcessUpdate) { - return; - } - int count = 0; const struct kinfo_proc* kprocs = kvm_getprocs(fpl->kd, KERN_PROC_PROC, 0, &count); @@ -521,14 +184,14 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { proc->st_uid = kproc->ki_uid; proc->starttime_ctime = kproc->ki_start.tv_sec; if (proc->starttime_ctime < 0) { - proc->starttime_ctime = super->host.realtimeMs / 1000; + proc->starttime_ctime = host->realtimeMs / 1000; } Process_fillStarttimeBuffer(proc); proc->user = UsersTable_getRef(host->usersTable, proc->st_uid); ProcessList_add(super, proc); FreeBSDProcessList_updateExe(kproc, proc); - FreeBSDProcessList_updateProcessName(fpl->kd, kproc, proc); + FreeBSDProcessList_updateProcessName(fhost->kd, kproc, proc); if (settings->ss->flags & PROCESS_FLAG_CWD) { FreeBSDProcessList_updateCwd(kproc, proc); @@ -559,7 +222,7 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { proc->user = UsersTable_getRef(host->usersTable, proc->st_uid); } if (settings->updateProcessNames) { - FreeBSDProcessList_updateProcessName(fpl->kd, kproc, proc); + FreeBSDProcessList_updateProcessName(fhost->kd, kproc, proc); } } @@ -567,11 +230,11 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { // from FreeBSD source /src/usr.bin/top/machine.c proc->m_virt = kproc->ki_size / ONE_K; - proc->m_resident = kproc->ki_rssize * pageSizeKb; + proc->m_resident = kproc->ki_rssize * fhost->pageSizeKb; proc->nlwp = kproc->ki_numthreads; proc->time = (kproc->ki_runtime + 5000) / 10000; - proc->percent_cpu = 100.0 * ((double)kproc->ki_pctcpu / (double)kernelFScale); + proc->percent_cpu = 100.0 * ((double)kproc->ki_pctcpu / (double)fhost->kernelFScale); proc->percent_mem = 100.0 * proc->m_resident / (double)(host->totalMem); Process_updateCPUFieldWidths(proc->percent_cpu); @@ -623,22 +286,3 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { proc->updated = true; } } - -Machine* Machine_new(UsersTable* usersTable, uid_t userId) { - Machine* this = xCalloc(1, sizeof(Machine)); - Machine_init(this, usersTable, userId); - return this; -} - -void Machine_delete(Machine* host) { - free(host); -} - -bool Machine_isCPUonline(const Machine* host, unsigned int id) { - assert(id < host->existingCPUs); - - // TODO: support offline CPUs and hot swapping - (void) host; (void) id; - - return true; -} diff --git a/freebsd/FreeBSDProcessList.h b/freebsd/FreeBSDProcessList.h index b7e29f8b1..55247eb79 100644 --- a/freebsd/FreeBSDProcessList.h +++ b/freebsd/FreeBSDProcessList.h @@ -7,56 +7,15 @@ Released under the GNU GPLv2+, see the COPYING file in the source distribution for its full text. */ -#include #include #include #include "Hashtable.h" #include "ProcessList.h" #include "UsersTable.h" -#include "zfs/ZfsArcStats.h" - - -typedef struct CPUData_ { - double userPercent; - double nicePercent; - double systemPercent; - double irqPercent; - double systemAllPercent; - - double frequency; - double temperature; -} CPUData; typedef struct FreeBSDProcessList_ { ProcessList super; - kvm_t* kd; - - unsigned long long int memWire; - unsigned long long int memActive; - - ZfsArcStats zfs; - - CPUData* cpus; - - unsigned long* cp_time_o; - unsigned long* cp_time_n; - - unsigned long* cp_times_o; - unsigned long* cp_times_n; - } FreeBSDProcessList; -ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList); - -void ProcessList_delete(ProcessList* this); - -void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate); - -Machine* Machine_new(UsersTable* usersTable, uid_t userId); - -bool Machine_isCPUonline(const Machine* host, unsigned int id); - -void Machine_delete(Machine* host); - #endif diff --git a/freebsd/Platform.c b/freebsd/Platform.c index 3ba2778ee..0588b0fae 100644 --- a/freebsd/Platform.c +++ b/freebsd/Platform.c @@ -34,20 +34,19 @@ in the source distribution for its full text. #include "FileDescriptorMeter.h" #include "HostnameMeter.h" #include "LoadAverageMeter.h" +#include "Machine.h" #include "Macros.h" #include "MemoryMeter.h" #include "MemorySwapMeter.h" #include "Meter.h" #include "NetworkIOMeter.h" -#include "ProcessList.h" #include "Settings.h" #include "SwapMeter.h" #include "SysArchMeter.h" #include "TasksMeter.h" #include "UptimeMeter.h" #include "XUtils.h" -#include "freebsd/FreeBSDProcess.h" -#include "freebsd/FreeBSDProcessList.h" +#include "freebsd/FreeBSDMachine.h" #include "generic/fdstat_sysctl.h" #include "zfs/ZfsArcMeter.h" #include "zfs/ZfsCompressedArcMeter.h" @@ -194,15 +193,15 @@ int Platform_getMaxPid(void) { double Platform_setCPUValues(Meter* this, unsigned int cpu) { const Machine* host = this->host; - const FreeBSDProcessList* fpl = (const FreeBSDProcessList*) host->pl; + const FreeBSDMachine* fhost = (const FreeBSDMachine*) host; unsigned int cpus = host->activeCPUs; const CPUData* cpuData; if (cpus == 1) { - // single CPU box has everything in fpl->cpus[0] - cpuData = &(fpl->cpus[0]); + // single CPU box has everything in fhost->cpus[0] + cpuData = &(fhost->cpus[0]); } else { - cpuData = &(fpl->cpus[cpu]); + cpuData = &(fhost->cpus[cpu]); } double percent; @@ -210,7 +209,7 @@ double Platform_setCPUValues(Meter* this, unsigned int cpu) { v[CPU_METER_NICE] = cpuData->nicePercent; v[CPU_METER_NORMAL] = cpuData->userPercent; - if (host->settings->detailedCPUTime) { + if (super->settings->detailedCPUTime) { v[CPU_METER_KERNEL] = cpuData->systemPercent; v[CPU_METER_IRQ] = cpuData->irqPercent; this->curItems = 4; @@ -231,8 +230,7 @@ double Platform_setCPUValues(Meter* this, unsigned int cpu) { void Platform_setMemoryValues(Meter* this) { const Machine* host = this->host; - const ProcessList* pl = host->pl; - const FreeBSDProcessList* fpl = (const FreeBSDProcessList*) pl; + const FreeBSDMachine* fhost = (const FreeBSDMachine*) host; this->total = host->totalMem; this->values[MEMORY_METER_USED] = host->usedMem; @@ -242,11 +240,11 @@ void Platform_setMemoryValues(Meter* this) { this->values[MEMORY_METER_CACHE] = host->cachedMem; // this->values[MEMORY_METER_AVAILABLE] = "available memory" - if (fpl->zfs.enabled) { + if (dhost->zfs.enabled) { // ZFS does not shrink below the value of zfs_arc_min. unsigned long long int shrinkableSize = 0; - if (fpl->zfs.size > fpl->zfs.min) - shrinkableSize = fpl->zfs.size - fpl->zfs.min; + if (dhost->zfs.size > dhost->zfs.min) + shrinkableSize = dhost->zfs.size - dhost->zfs.min; this->values[MEMORY_METER_USED] -= shrinkableSize; this->values[MEMORY_METER_CACHE] += shrinkableSize; // this->values[MEMORY_METER_AVAILABLE] += shrinkableSize; @@ -263,15 +261,15 @@ void Platform_setSwapValues(Meter* this) { } void Platform_setZfsArcValues(Meter* this) { - const FreeBSDProcessList* fpl = (const FreeBSDProcessList*) this->host->pl; + const FreeBSDMachine* fhost = (const FreeBSDMachine*) this->host; - ZfsArcMeter_readStats(this, &(fpl->zfs)); + ZfsArcMeter_readStats(this, &fhost->zfs); } void Platform_setZfsCompressedArcValues(Meter* this) { - const FreeBSDProcessList* fpl = (const FreeBSDProcessList*) this->host->pl; + const FreeBSDMachine* fhost = (const FreeBSDMachine*) this->host; - ZfsCompressedArcMeter_readStats(this, &(fpl->zfs)); + ZfsCompressedArcMeter_readStats(this, &fhost->zfs); } char* Platform_getProcessEnv(pid_t pid) { diff --git a/linux/HugePageMeter.c b/linux/HugePageMeter.c index 0e4294443..ec3804eee 100644 --- a/linux/HugePageMeter.c +++ b/linux/HugePageMeter.c @@ -16,7 +16,7 @@ in the source distribution for its full text. #include "Object.h" #include "ProcessList.h" #include "RichString.h" -#include "linux/LinuxProcessList.h" +#include "linux/LinuxMachine.h" static const char* HugePageMeter_active_labels[4] = { NULL, NULL, NULL, NULL }; @@ -43,8 +43,8 @@ static void HugePageMeter_updateValues(Meter* this) { memory_t usedTotal = 0; unsigned nextUsed = 0; - const LinuxProcessList* lpl = (const LinuxProcessList*) this->host->pl; - this->total = lpl->totalHugePageMem; + const LinuxMachine* host = (const LinuxMachine*) this->host; + this->total = host->totalHugePageMem; this->values[0] = 0; HugePageMeter_active_labels[0] = " used:"; for (unsigned i = 1; i < ARRAYSIZE(HugePageMeter_active_labels); i++) { @@ -52,7 +52,7 @@ static void HugePageMeter_updateValues(Meter* this) { HugePageMeter_active_labels[i] = NULL; } for (unsigned i = 0; i < HTOP_HUGEPAGE_COUNT; i++) { - memory_t value = lpl->usedHugePageMem[i]; + memory_t value = host->usedHugePageMem[i]; if (value != MEMORY_MAX) { this->values[nextUsed] = value; usedTotal += value; diff --git a/linux/LibSensors.c b/linux/LibSensors.c index 9a27fe57c..ff084b648 100644 --- a/linux/LibSensors.c +++ b/linux/LibSensors.c @@ -16,7 +16,7 @@ #include "Macros.h" #include "XUtils.h" -#include "linux/LinuxProcessList.h" +#include "linux/LinuxMachine.h" #ifdef BUILD_STATIC diff --git a/linux/LibSensors.h b/linux/LibSensors.h index aa899793e..2b9801bcd 100644 --- a/linux/LibSensors.h +++ b/linux/LibSensors.h @@ -1,7 +1,7 @@ #ifndef HEADER_LibSensors #define HEADER_LibSensors -#include "linux/LinuxProcessList.h" +#include "linux/LinuxMachine.h" int LibSensors_init(void); diff --git a/linux/LinuxMachine.c b/linux/LinuxMachine.c new file mode 100644 index 000000000..48d0e80e1 --- /dev/null +++ b/linux/LinuxMachine.c @@ -0,0 +1,707 @@ +/* +htop - LinuxMachine.c +(C) 2014 Hisham H. Muhammad +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "config.h" // IWYU pragma: keep + +#include "linux/LinuxMachine.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Compat.h" +#include "XUtils.h" +#include "linux/LinuxMachine.h" +#include "linux/Platform.h" // needed for GNU/hurd to get PATH_MAX // IWYU pragma: keep + +#ifdef HAVE_SENSORS_SENSORS_H +#include "LibSensors.h" +#endif + +#ifndef O_PATH +#define O_PATH 010000000 // declare for ancient glibc versions +#endif + +/* Similar to get_nprocs_conf(3) / _SC_NPROCESSORS_CONF + * https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/unix/sysv/linux/getsysstats.c;hb=HEAD + */ +static void LinuxMachine_updateCPUcount(LinuxMachine* this) { + unsigned int existing = 0, active = 0; + Machine* super = &this->super; + + // Initialize the cpuData array before anything else. + if (!this->cpuData) { + this->cpuData = xCalloc(2, sizeof(CPUData)); + this->cpuData[0].online = true; /* average is always "online" */ + this->cpuData[1].online = true; + super->activeCPUs = 1; + super->existingCPUs = 1; + } + + DIR* dir = opendir("/sys/devices/system/cpu"); + if (!dir) + return; + + unsigned int currExisting = super->existingCPUs; + + const struct dirent* entry; + while ((entry = readdir(dir)) != NULL) { + if (entry->d_type != DT_DIR && entry->d_type != DT_UNKNOWN) + continue; + + if (!String_startsWith(entry->d_name, "cpu")) + continue; + + char* endp; + unsigned long int id = strtoul(entry->d_name + 3, &endp, 10); + if (id == ULONG_MAX || endp == entry->d_name + 3 || *endp != '\0') + continue; + +#ifdef HAVE_OPENAT + int cpuDirFd = openat(dirfd(dir), entry->d_name, O_DIRECTORY | O_PATH | O_NOFOLLOW); + if (cpuDirFd < 0) + continue; +#else + char cpuDirFd[4096]; + xSnprintf(cpuDirFd, sizeof(cpuDirFd), "/sys/devices/system/cpu/%s", entry->d_name); +#endif + + existing++; + + /* readdir() iterates with no specific order */ + unsigned int max = MAXIMUM(existing, id + 1); + if (max > currExisting) { + this->cpuData = xReallocArrayZero(this->cpuData, currExisting ? (currExisting + 1) : 0, max + /* aggregate */ 1, sizeof(CPUData)); + this->cpuData[0].online = true; /* average is always "online" */ + currExisting = max; + } + + char buffer[8]; + ssize_t res = xReadfileat(cpuDirFd, "online", buffer, sizeof(buffer)); + /* If the file "online" does not exist or on failure count as active */ + if (res < 1 || buffer[0] != '0') { + active++; + this->cpuData[id + 1].online = true; + } else { + this->cpuData[id + 1].online = false; + } + + Compat_openatArgClose(cpuDirFd); + } + + closedir(dir); + + // return if no CPU is found + if (existing < 1) + return; + +#ifdef HAVE_SENSORS_SENSORS_H + /* When started with offline CPUs, libsensors does not monitor those, + * even when they become online. */ + if (super->existingCPUs != 0 && (active > super->activeCPUs || currExisting > super->existingCPUs)) + LibSensors_reload(); +#endif + + super->activeCPUs = active; + assert(existing == currExisting); + super->existingCPUs = currExisting; +} + +static void LinuxMachine_scanMemoryInfo(LinuxMachine* this) { + Machine* host = &this->super; + memory_t availableMem = 0; + memory_t freeMem = 0; + memory_t totalMem = 0; + memory_t buffersMem = 0; + memory_t cachedMem = 0; + memory_t sharedMem = 0; + memory_t swapTotalMem = 0; + memory_t swapCacheMem = 0; + memory_t swapFreeMem = 0; + memory_t sreclaimableMem = 0; + memory_t zswapCompMem = 0; + memory_t zswapOrigMem = 0; + + FILE* file = fopen(PROCMEMINFOFILE, "r"); + if (!file) + CRT_fatalError("Cannot open " PROCMEMINFOFILE); + + char buffer[128]; + while (fgets(buffer, sizeof(buffer), file)) { + + #define tryRead(label, variable) \ + if (String_startsWith(buffer, label)) { \ + memory_t parsed_; \ + if (sscanf(buffer + strlen(label), "%llu kB", &parsed_) == 1) { \ + (variable) = parsed_; \ + } \ + break; \ + } else (void) 0 /* Require a ";" after the macro use. */ + + switch (buffer[0]) { + case 'M': + tryRead("MemAvailable:", availableMem); + tryRead("MemFree:", freeMem); + tryRead("MemTotal:", totalMem); + break; + case 'B': + tryRead("Buffers:", buffersMem); + break; + case 'C': + tryRead("Cached:", cachedMem); + break; + case 'S': + switch (buffer[1]) { + case 'h': + tryRead("Shmem:", sharedMem); + break; + case 'w': + tryRead("SwapTotal:", swapTotalMem); + tryRead("SwapCached:", swapCacheMem); + tryRead("SwapFree:", swapFreeMem); + break; + case 'R': + tryRead("SReclaimable:", sreclaimableMem); + break; + } + break; + case 'Z': + tryRead("Zswap:", zswapCompMem); + tryRead("Zswapped:", zswapOrigMem); + break; + } + + #undef tryRead + } + + fclose(file); + + /* + * Compute memory partition like procps(free) + * https://gitlab.com/procps-ng/procps/-/blob/master/proc/sysinfo.c + * + * Adjustments: + * - Shmem in part of Cached (see https://lore.kernel.org/patchwork/patch/648763/), + * do not show twice by subtracting from Cached and do not subtract twice from used. + */ + host->totalMem = totalMem; + host->cachedMem = cachedMem + sreclaimableMem - sharedMem; + host->sharedMem = sharedMem; + const memory_t usedDiff = freeMem + cachedMem + sreclaimableMem + buffersMem; + host->usedMem = (totalMem >= usedDiff) ? totalMem - usedDiff : totalMem - freeMem; + host->buffersMem = buffersMem; + host->availableMem = availableMem != 0 ? MINIMUM(availableMem, totalMem) : freeMem; + host->totalSwap = swapTotalMem; + host->usedSwap = swapTotalMem - swapFreeMem - swapCacheMem; + host->cachedSwap = swapCacheMem; + this->zswap.usedZswapComp = zswapCompMem; + this->zswap.usedZswapOrig = zswapOrigMem; +} + +static void LinuxMachine_scanHugePages(LinuxMachine* this) { + this->totalHugePageMem = 0; + for (unsigned i = 0; i < HTOP_HUGEPAGE_COUNT; i++) { + this->usedHugePageMem[i] = MEMORY_MAX; + } + + DIR* dir = opendir("/sys/kernel/mm/hugepages"); + if (!dir) + return; + + const struct dirent* entry; + while ((entry = readdir(dir)) != NULL) { + const char* name = entry->d_name; + + /* Ignore all non-directories */ + if (entry->d_type != DT_DIR && entry->d_type != DT_UNKNOWN) + continue; + + if (!String_startsWith(name, "hugepages-")) + continue; + + char* endptr; + unsigned long int hugePageSize = strtoul(name + strlen("hugepages-"), &endptr, 10); + if (!endptr || *endptr != 'k') + continue; + + char content[64]; + char hugePagePath[128]; + ssize_t r; + + xSnprintf(hugePagePath, sizeof(hugePagePath), "/sys/kernel/mm/hugepages/%s/nr_hugepages", name); + r = xReadfile(hugePagePath, content, sizeof(content)); + if (r <= 0) + continue; + + memory_t total = strtoull(content, NULL, 10); + if (total == 0) + continue; + + xSnprintf(hugePagePath, sizeof(hugePagePath), "/sys/kernel/mm/hugepages/%s/free_hugepages", name); + r = xReadfile(hugePagePath, content, sizeof(content)); + if (r <= 0) + continue; + + memory_t free = strtoull(content, NULL, 10); + + int shift = ffsl(hugePageSize) - 1 - (HTOP_HUGEPAGE_BASE_SHIFT - 10); + assert(shift >= 0 && shift < HTOP_HUGEPAGE_COUNT); + + this->totalHugePageMem += total * hugePageSize; + this->usedHugePageMem[shift] = (total - free) * hugePageSize; + } + + closedir(dir); +} + +static inline void LinuxMachine_scanZswapInfo(LinuxMachine* this) { + const Machine* host = &this->super; + long max_pool_percent = 0; + char buf[256]; + int r; + + r = xReadfile("/sys/module/zswap/parameters/max_pool_percent", buf, 256); + if (r <= 0) { + return; + } + max_pool_percent = strtol(buf, NULL, 10); + if (max_pool_percent < 0 || max_pool_percent > 100) { + return; + } + + this->zswap.totalZswapPool = host->totalMem * max_pool_percent / 100; + /* the rest of the metrics are set in LinuxMachine_scanMemoryInfo() */ +} + +static void LinuxMachine_scanZramInfo(LinuxMachine* this) { + memory_t totalZram = 0; + memory_t usedZramComp = 0; + memory_t usedZramOrig = 0; + + char mm_stat[34]; + char disksize[34]; + + unsigned int i = 0; + for (;;) { + xSnprintf(mm_stat, sizeof(mm_stat), "/sys/block/zram%u/mm_stat", i); + xSnprintf(disksize, sizeof(disksize), "/sys/block/zram%u/disksize", i); + i++; + FILE* disksize_file = fopen(disksize, "r"); + FILE* mm_stat_file = fopen(mm_stat, "r"); + if (disksize_file == NULL || mm_stat_file == NULL) { + if (disksize_file) { + fclose(disksize_file); + } + if (mm_stat_file) { + fclose(mm_stat_file); + } + break; + } + memory_t size = 0; + memory_t orig_data_size = 0; + memory_t compr_data_size = 0; + + if (!fscanf(disksize_file, "%llu\n", &size) || + !fscanf(mm_stat_file, " %llu %llu", &orig_data_size, &compr_data_size)) { + fclose(disksize_file); + fclose(mm_stat_file); + break; + } + + totalZram += size; + usedZramComp += compr_data_size; + usedZramOrig += orig_data_size; + + fclose(disksize_file); + fclose(mm_stat_file); + } + + this->zram.totalZram = totalZram / 1024; + this->zram.usedZramComp = usedZramComp / 1024; + this->zram.usedZramOrig = usedZramOrig / 1024; +} + +static void LinuxMachine_scanZfsArcstats(LinuxMachine* this) { + memory_t dbufSize = 0; + memory_t dnodeSize = 0; + memory_t bonusSize = 0; + + FILE* file = fopen(PROCARCSTATSFILE, "r"); + if (file == NULL) { + this->zfs.enabled = 0; + return; + } + char buffer[128]; + while (fgets(buffer, 128, file)) { + #define tryRead(label, variable) \ + if (String_startsWith(buffer, label)) { \ + sscanf(buffer + strlen(label), " %*2u %32llu", variable); \ + break; \ + } else (void) 0 /* Require a ";" after the macro use. */ + #define tryReadFlag(label, variable, flag) \ + if (String_startsWith(buffer, label)) { \ + (flag) = sscanf(buffer + strlen(label), " %*2u %32llu", variable); \ + break; \ + } else (void) 0 /* Require a ";" after the macro use. */ + + switch (buffer[0]) { + case 'c': + tryRead("c_min", &this->zfs.min); + tryRead("c_max", &this->zfs.max); + tryReadFlag("compressed_size", &this->zfs.compressed, this->zfs.isCompressed); + break; + case 'u': + tryRead("uncompressed_size", &this->zfs.uncompressed); + break; + case 's': + tryRead("size", &this->zfs.size); + break; + case 'h': + tryRead("hdr_size", &this->zfs.header); + break; + case 'd': + tryRead("dbuf_size", &dbufSize); + tryRead("dnode_size", &dnodeSize); + break; + case 'b': + tryRead("bonus_size", &bonusSize); + break; + case 'a': + tryRead("anon_size", &this->zfs.anon); + break; + case 'm': + tryRead("mfu_size", &this->zfs.MFU); + tryRead("mru_size", &this->zfs.MRU); + break; + } + #undef tryRead + #undef tryReadFlag + } + fclose(file); + + this->zfs.enabled = (this->zfs.size > 0 ? 1 : 0); + this->zfs.size /= 1024; + this->zfs.min /= 1024; + this->zfs.max /= 1024; + this->zfs.MFU /= 1024; + this->zfs.MRU /= 1024; + this->zfs.anon /= 1024; + this->zfs.header /= 1024; + this->zfs.other = (dbufSize + dnodeSize + bonusSize) / 1024; + if ( this->zfs.isCompressed ) { + this->zfs.compressed /= 1024; + this->zfs.uncompressed /= 1024; + } +} + +static void LinuxMachine_scanCPUTime(LinuxMachine* this) { + const Machine* super = &this->super; + + LinuxMachine_updateCPUcount(this); + + FILE* file = fopen(PROCSTATFILE, "r"); + if (!file) + CRT_fatalError("Cannot open " PROCSTATFILE); + + unsigned int lastAdjCpuId = 0; + + for (unsigned int i = 0; i <= super->existingCPUs; i++) { + char buffer[PROC_LINE_LENGTH + 1]; + unsigned long long int usertime, nicetime, systemtime, idletime; + unsigned long long int ioWait = 0, irq = 0, softIrq = 0, steal = 0, guest = 0, guestnice = 0; + + const char* ok = fgets(buffer, sizeof(buffer), file); + if (!ok) + break; + + // cpu fields are sorted first + if (!String_startsWith(buffer, "cpu")) + break; + + // Depending on your kernel version, + // 5, 7, 8 or 9 of these fields will be set. + // The rest will remain at zero. + unsigned int adjCpuId; + if (i == 0) { + (void) sscanf(buffer, "cpu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu", &usertime, &nicetime, &systemtime, &idletime, &ioWait, &irq, &softIrq, &steal, &guest, &guestnice); + adjCpuId = 0; + } else { + unsigned int cpuid; + (void) sscanf(buffer, "cpu%4u %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu", &cpuid, &usertime, &nicetime, &systemtime, &idletime, &ioWait, &irq, &softIrq, &steal, &guest, &guestnice); + adjCpuId = cpuid + 1; + } + + if (adjCpuId > super->existingCPUs) + break; + + for (unsigned int j = lastAdjCpuId + 1; j < adjCpuId; j++) { + // Skipped an ID, but /proc/stat is ordered => got offline CPU + memset(&(this->cpuData[j]), '\0', sizeof(CPUData)); + } + lastAdjCpuId = adjCpuId; + + // Guest time is already accounted in usertime + usertime -= guest; + nicetime -= guestnice; + // Fields existing on kernels >= 2.6 + // (and RHEL's patched kernel 2.4...) + unsigned long long int idlealltime = idletime + ioWait; + unsigned long long int systemalltime = systemtime + irq + softIrq; + unsigned long long int virtalltime = guest + guestnice; + unsigned long long int totaltime = usertime + nicetime + systemalltime + idlealltime + steal + virtalltime; + CPUData* cpuData = &(this->cpuData[adjCpuId]); + // Since we do a subtraction (usertime - guest) and cputime64_to_clock_t() + // used in /proc/stat rounds down numbers, it can lead to a case where the + // integer overflow. + cpuData->userPeriod = saturatingSub(usertime, cpuData->userTime); + cpuData->nicePeriod = saturatingSub(nicetime, cpuData->niceTime); + cpuData->systemPeriod = saturatingSub(systemtime, cpuData->systemTime); + cpuData->systemAllPeriod = saturatingSub(systemalltime, cpuData->systemAllTime); + cpuData->idleAllPeriod = saturatingSub(idlealltime, cpuData->idleAllTime); + cpuData->idlePeriod = saturatingSub(idletime, cpuData->idleTime); + cpuData->ioWaitPeriod = saturatingSub(ioWait, cpuData->ioWaitTime); + cpuData->irqPeriod = saturatingSub(irq, cpuData->irqTime); + cpuData->softIrqPeriod = saturatingSub(softIrq, cpuData->softIrqTime); + cpuData->stealPeriod = saturatingSub(steal, cpuData->stealTime); + cpuData->guestPeriod = saturatingSub(virtalltime, cpuData->guestTime); + cpuData->totalPeriod = saturatingSub(totaltime, cpuData->totalTime); + cpuData->userTime = usertime; + cpuData->niceTime = nicetime; + cpuData->systemTime = systemtime; + cpuData->systemAllTime = systemalltime; + cpuData->idleAllTime = idlealltime; + cpuData->idleTime = idletime; + cpuData->ioWaitTime = ioWait; + cpuData->irqTime = irq; + cpuData->softIrqTime = softIrq; + cpuData->stealTime = steal; + cpuData->guestTime = virtalltime; + cpuData->totalTime = totaltime; + } + + this->period = (double)this->cpuData[0].totalPeriod / super->activeCPUs; + + char buffer[PROC_LINE_LENGTH + 1]; + while (fgets(buffer, sizeof(buffer), file)) { + if (String_startsWith(buffer, "procs_running")) { + super->pl->runningTasks = strtoul(buffer + strlen("procs_running"), NULL, 10); + break; + } + } + + fclose(file); +} + +static int scanCPUFrequencyFromSysCPUFreq(LinuxMachine* this) { + const Machine* super = &this->super; + int numCPUsWithFrequency = 0; + unsigned long totalFrequency = 0; + + /* + * On some AMD and Intel CPUs read()ing scaling_cur_freq is quite slow (> 1ms). This delay + * accumulates for every core. For details see issue#471. + * If the read on CPU 0 takes longer than 500us bail out and fall back to reading the + * frequencies from /proc/cpuinfo. + * Once the condition has been met, bail out early for the next couple of scans. + */ + static int timeout = 0; + + if (timeout > 0) { + timeout--; + return -1; + } + + for (unsigned int i = 0; i < super->existingCPUs; ++i) { + if (!Machine_isCPUonline(super, i)) + continue; + + char pathBuffer[64]; + xSnprintf(pathBuffer, sizeof(pathBuffer), "/sys/devices/system/cpu/cpu%u/cpufreq/scaling_cur_freq", i); + + struct timespec start; + if (i == 0) + clock_gettime(CLOCK_MONOTONIC, &start); + + FILE* file = fopen(pathBuffer, "r"); + if (!file) + return -errno; + + unsigned long frequency; + if (fscanf(file, "%lu", &frequency) == 1) { + /* convert kHz to MHz */ + frequency = frequency / 1000; + this->cpuData[i + 1].frequency = frequency; + numCPUsWithFrequency++; + totalFrequency += frequency; + } + + fclose(file); + + if (i == 0) { + struct timespec end; + clock_gettime(CLOCK_MONOTONIC, &end); + const time_t timeTakenUs = (end.tv_sec - start.tv_sec) * 1000000 + (end.tv_nsec - start.tv_nsec) / 1000; + if (timeTakenUs > 500) { + timeout = 30; + return -1; + } + } + } + + if (numCPUsWithFrequency > 0) + this->cpuData[0].frequency = (double)totalFrequency / numCPUsWithFrequency; + + return 0; +} + +static void scanCPUFrequencyFromCPUinfo(LinuxMachine* this) { + const Machine* super = &this->super; + + FILE* file = fopen(PROCCPUINFOFILE, "r"); + if (file == NULL) + return; + + int numCPUsWithFrequency = 0; + double totalFrequency = 0; + int cpuid = -1; + + while (!feof(file)) { + double frequency; + char buffer[PROC_LINE_LENGTH]; + + if (fgets(buffer, PROC_LINE_LENGTH, file) == NULL) + break; + + if (sscanf(buffer, "processor : %d", &cpuid) == 1) { + continue; + } else if ( + (sscanf(buffer, "cpu MHz : %lf", &frequency) == 1) || + (sscanf(buffer, "clock : %lfMHz", &frequency) == 1) + ) { + if (cpuid < 0 || (unsigned int)cpuid > (super->existingCPUs - 1)) { + continue; + } + + CPUData* cpuData = &(this->cpuData[cpuid + 1]); + /* do not override sysfs data */ + if (isnan(cpuData->frequency)) { + cpuData->frequency = frequency; + } + numCPUsWithFrequency++; + totalFrequency += frequency; + } else if (buffer[0] == '\n') { + cpuid = -1; + } + } + fclose(file); + + if (numCPUsWithFrequency > 0) { + this->cpuData[0].frequency = totalFrequency / numCPUsWithFrequency; + } +} + +static void LinuxMachine_scanCPUFrequency(LinuxMachine* this) { + const Machine* super = &this->super; + + for (unsigned int i = 0; i <= super->existingCPUs; i++) + this->cpuData[i].frequency = NAN; + + if (scanCPUFrequencyFromSysCPUFreq(this) == 0) + return; + + scanCPUFrequencyFromCPUinfo(this); +} + +void Machine_scan(Machine* super) { + LinuxMachine* this = (LinuxMachine*) super; + + LinuxMachine_scanMemoryInfo(this); + LinuxMachine_scanHugePages(this); + LinuxMachine_scanZfsArcstats(this); + LinuxMachine_scanZramInfo(this); + LinuxMachine_scanZswapInfo(this); + LinuxMachine_scanCPUTime(this); + + const Settings* settings = super->settings; + if (settings->showCPUFrequency) + LinuxMachine_scanCPUFrequency(this); + + #ifdef HAVE_SENSORS_SENSORS_H + if (settings->showCPUTemperature) + LibSensors_getCPUTemperatures(this->cpuData, super->existingCPUs, super->activeCPUs); + #endif +} + +Machine* Machine_new(UsersTable* usersTable, uid_t userId) { + LinuxMachine* this = xCalloc(1, sizeof(LinuxMachine)); + Machine* super = &this->super; + + Machine_init(super, usersTable, userId); + + // Initialize page size + if ((this->pageSize = sysconf(_SC_PAGESIZE)) == -1) + CRT_fatalError("Cannot get pagesize by sysconf(_SC_PAGESIZE)"); + this->pageSizeKB = this->pageSize / ONE_K; + + // Initialize clock ticks + if ((this->jiffies = sysconf(_SC_CLK_TCK)) == -1) + CRT_fatalError("Cannot get clock ticks by sysconf(_SC_CLK_TCK)"); + + // Read btime (the kernel boot time, as number of seconds since the epoch) + FILE* statfile = fopen(PROCSTATFILE, "r"); + if (statfile == NULL) + CRT_fatalError("Cannot open " PROCSTATFILE); + + this->boottime = -1; + + while (true) { + char buffer[PROC_LINE_LENGTH + 1]; + if (fgets(buffer, sizeof(buffer), statfile) == NULL) + break; + if (String_startsWith(buffer, "btime ") == false) + continue; + if (sscanf(buffer, "btime %lld\n", &this->boottime) == 1) + break; + CRT_fatalError("Failed to parse btime from " PROCSTATFILE); + } + fclose(statfile); + + if (this->boottime == -1) + CRT_fatalError("No btime in " PROCSTATFILE); + + // Initialize CPU count + LinuxMachine_updateCPUcount(this); + + return super; +} + +void Machine_delete(Machine* super) { + LinuxMachine* this = (LinuxMachine*) super; + Machine_done(super); + free(this->cpuData); + free(this); +} + +bool Machine_isCPUonline(const Machine* super, unsigned int id) { + const LinuxMachine* this = (const LinuxMachine*) super; + + assert(id < super->existingCPUs); + return this->cpuData[id + 1].online; +} diff --git a/linux/LinuxMachine.h b/linux/LinuxMachine.h new file mode 100644 index 000000000..7dba905fd --- /dev/null +++ b/linux/LinuxMachine.h @@ -0,0 +1,110 @@ +#ifndef HEADER_LinuxMachine +#define HEADER_LinuxMachine +/* +htop - LinuxMachine.h +(C) 2014 Hisham H. Muhammad +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "config.h" + +#include +#include + +#include "Machine.h" +#include "UsersTable.h" +#include "linux/ZramStats.h" +#include "linux/ZswapStats.h" +#include "zfs/ZfsArcStats.h" + +#define HTOP_HUGEPAGE_BASE_SHIFT 16 +#define HTOP_HUGEPAGE_COUNT 24 + +typedef struct CPUData_ { + unsigned long long int totalTime; + unsigned long long int userTime; + unsigned long long int systemTime; + unsigned long long int systemAllTime; + unsigned long long int idleAllTime; + unsigned long long int idleTime; + unsigned long long int niceTime; + unsigned long long int ioWaitTime; + unsigned long long int irqTime; + unsigned long long int softIrqTime; + unsigned long long int stealTime; + unsigned long long int guestTime; + + unsigned long long int totalPeriod; + unsigned long long int userPeriod; + unsigned long long int systemPeriod; + unsigned long long int systemAllPeriod; + unsigned long long int idleAllPeriod; + unsigned long long int idlePeriod; + unsigned long long int nicePeriod; + unsigned long long int ioWaitPeriod; + unsigned long long int irqPeriod; + unsigned long long int softIrqPeriod; + unsigned long long int stealPeriod; + unsigned long long int guestPeriod; + + double frequency; + + #ifdef HAVE_SENSORS_SENSORS_H + double temperature; + #endif + + bool online; +} CPUData; + +typedef struct LinuxMachine_ { + Machine super; + + long jiffies; + long long boottime; + int pageSize; + int pageSizeKB; + + double period; + + CPUData* cpuData; + + memory_t totalHugePageMem; + memory_t usedHugePageMem[HTOP_HUGEPAGE_COUNT]; + + memory_t availableMem; + + ZfsArcStats zfs; + ZramStats zram; + ZswapStats zswap; +} LinuxMachine; + +#ifndef PROCDIR +#define PROCDIR "/proc" +#endif + +#ifndef PROCCPUINFOFILE +#define PROCCPUINFOFILE PROCDIR "/cpuinfo" +#endif + +#ifndef PROCSTATFILE +#define PROCSTATFILE PROCDIR "/stat" +#endif + +#ifndef PROCMEMINFOFILE +#define PROCMEMINFOFILE PROCDIR "/meminfo" +#endif + +#ifndef PROCARCSTATSFILE +#define PROCARCSTATSFILE PROCDIR "/spl/kstat/zfs/arcstats" +#endif + +#ifndef PROCTTYDRIVERSFILE +#define PROCTTYDRIVERSFILE PROCDIR "/tty/drivers" +#endif + +#ifndef PROC_LINE_LENGTH +#define PROC_LINE_LENGTH 4096 +#endif + +#endif diff --git a/linux/LinuxProcess.c b/linux/LinuxProcess.c index a3a4c8218..b815c5b55 100644 --- a/linux/LinuxProcess.c +++ b/linux/LinuxProcess.c @@ -22,12 +22,9 @@ in the source distribution for its full text. #include "Scheduling.h" #include "XUtils.h" #include "linux/IOPriority.h" +#include "linux/LinuxMachine.h" -/* semi-global */ -int pageSize; -int pageSizeKB; - const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = { [0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, }, [PID] = { .name = "PID", .title = "PID", .description = "Process/thread ID", .flags = 0, .pidColumn = true, }, @@ -197,6 +194,7 @@ bool LinuxProcess_changeAutogroupPriorityBy(Process* this, Arg delta) { static void LinuxProcess_writeField(const Process* this, RichString* str, ProcessField field) { const LinuxProcess* lp = (const LinuxProcess*) this; + const LinuxMachine* lhost = (const LinuxMachine*) this->host; bool coloring = this->host->settings->highlightMegabytes; char buffer[256]; buffer[255] = '\0'; int attr = CRT_colors[DEFAULT_COLOR]; @@ -204,18 +202,18 @@ static void LinuxProcess_writeField(const Process* this, RichString* str, Proces switch (field) { case CMINFLT: Process_printCount(str, lp->cminflt, coloring); return; case CMAJFLT: Process_printCount(str, lp->cmajflt, coloring); return; - case M_DRS: Process_printBytes(str, lp->m_drs * pageSize, coloring); return; + case M_DRS: Process_printBytes(str, lp->m_drs * lhost->pageSize, coloring); return; case M_LRS: if (lp->m_lrs) { - Process_printBytes(str, lp->m_lrs * pageSize, coloring); + Process_printBytes(str, lp->m_lrs * lhost->pageSize, coloring); return; } attr = CRT_colors[PROCESS_SHADOW]; xSnprintf(buffer, n, " N/A "); break; - case M_TRS: Process_printBytes(str, lp->m_trs * pageSize, coloring); return; - case M_SHARE: Process_printBytes(str, lp->m_share * pageSize, coloring); return; + case M_TRS: Process_printBytes(str, lp->m_trs * lhost->pageSize, coloring); return; + case M_SHARE: Process_printBytes(str, lp->m_share * lhost->pageSize, coloring); return; case M_PSS: Process_printKBytes(str, lp->m_pss, coloring); return; case M_SWAP: Process_printKBytes(str, lp->m_swap, coloring); return; case M_PSSWP: Process_printKBytes(str, lp->m_psswp, coloring); return; diff --git a/linux/LinuxProcessList.c b/linux/LinuxProcessList.c index 911fc3e1e..e0a33e287 100644 --- a/linux/LinuxProcessList.c +++ b/linux/LinuxProcessList.c @@ -49,6 +49,7 @@ in the source distribution for its full text. #include "Settings.h" #include "XUtils.h" #include "linux/CGroupUtils.h" +#include "linux/LinuxMachine.h" #include "linux/LinuxProcess.h" #include "linux/Platform.h" // needed for GNU/hurd to get PATH_MAX // IWYU pragma: keep @@ -58,23 +59,11 @@ in the source distribution for its full text. #include #endif -#ifdef HAVE_SENSORS_SENSORS_H -#include "LibSensors.h" -#endif - -#ifndef O_PATH -#define O_PATH 010000000 // declare for ancient glibc versions -#endif - /* Not exposed yet. Defined at include/linux/sched.h */ #ifndef PF_KTHREAD #define PF_KTHREAD 0x00200000 #endif -static long long btime = -1; - -static long jiffy; - static FILE* fopenat(openat_arg_t openatArg, const char* pathname, const char* mode) { assert(String_eq(mode, "r")); /* only currently supported mode */ @@ -211,144 +200,22 @@ static void LinuxProcessList_initNetlinkSocket(LinuxProcessList* this) { #endif -static void LinuxProcessList_updateCPUcount(ProcessList* super) { - /* Similar to get_nprocs_conf(3) / _SC_NPROCESSORS_CONF - * https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/unix/sysv/linux/getsysstats.c;hb=HEAD - */ - - Machine* host = super->host; - LinuxProcessList* this = (LinuxProcessList*) super; - unsigned int existing = 0, active = 0; - - // Initialize the cpuData array before anything else. - if (!this->cpuData) { - this->cpuData = xCalloc(2, sizeof(CPUData)); - this->cpuData[0].online = true; /* average is always "online" */ - this->cpuData[1].online = true; - host->activeCPUs = 1; - host->existingCPUs = 1; - } - - DIR* dir = opendir("/sys/devices/system/cpu"); - if (!dir) - return; - - unsigned int currExisting = host->existingCPUs; - - const struct dirent* entry; - while ((entry = readdir(dir)) != NULL) { - if (entry->d_type != DT_DIR && entry->d_type != DT_UNKNOWN) - continue; - - if (!String_startsWith(entry->d_name, "cpu")) - continue; - - char* endp; - unsigned long int id = strtoul(entry->d_name + 3, &endp, 10); - if (id == ULONG_MAX || endp == entry->d_name + 3 || *endp != '\0') - continue; - -#ifdef HAVE_OPENAT - int cpuDirFd = openat(dirfd(dir), entry->d_name, O_DIRECTORY | O_PATH | O_NOFOLLOW); - if (cpuDirFd < 0) - continue; -#else - char cpuDirFd[4096]; - xSnprintf(cpuDirFd, sizeof(cpuDirFd), "/sys/devices/system/cpu/%s", entry->d_name); -#endif - - existing++; - - /* readdir() iterates with no specific order */ - unsigned int max = MAXIMUM(existing, id + 1); - if (max > currExisting) { - this->cpuData = xReallocArrayZero(this->cpuData, currExisting ? (currExisting + 1) : 0, max + /* aggregate */ 1, sizeof(CPUData)); - this->cpuData[0].online = true; /* average is always "online" */ - currExisting = max; - } - - char buffer[8]; - ssize_t res = xReadfileat(cpuDirFd, "online", buffer, sizeof(buffer)); - /* If the file "online" does not exist or on failure count as active */ - if (res < 1 || buffer[0] != '0') { - active++; - this->cpuData[id + 1].online = true; - } else { - this->cpuData[id + 1].online = false; - } - - Compat_openatArgClose(cpuDirFd); - } - - closedir(dir); - - // return if no CPU is found - if (existing < 1) - return; - -#ifdef HAVE_SENSORS_SENSORS_H - /* When started with offline CPUs, libsensors does not monitor those, - * even when they become online. */ - if (host->existingCPUs != 0 && (active > host->activeCPUs || currExisting > host->existingCPUs)) - LibSensors_reload(); -#endif - - host->activeCPUs = active; - assert(existing == currExisting); - host->existingCPUs = currExisting; -} - ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList) { LinuxProcessList* this = xCalloc(1, sizeof(LinuxProcessList)); - ProcessList* pl = &(this->super); + ProcessList* super = &this->super; - ProcessList_init(pl, Class(LinuxProcess), host, pidMatchList); + ProcessList_init(super, Class(LinuxProcess), host, pidMatchList); LinuxProcessList_initTtyDrivers(this); - // Initialize page size - pageSize = sysconf(_SC_PAGESIZE); - if (pageSize == -1) - CRT_fatalError("Cannot get pagesize by sysconf(_SC_PAGESIZE)"); - pageSizeKB = pageSize / ONE_K; - - // Initialize clock ticks - jiffy = sysconf(_SC_CLK_TCK); - if (jiffy == -1) - CRT_fatalError("Cannot get clock ticks by sysconf(_SC_CLK_TCK)"); - // Test /proc/PID/smaps_rollup availability (faster to parse, Linux 4.14+) this->haveSmapsRollup = (access(PROCDIR "/self/smaps_rollup", R_OK) == 0); - // Read btime (the kernel boot time, as number of seconds since the epoch) - FILE* statfile = fopen(PROCSTATFILE, "r"); - if (statfile == NULL) - CRT_fatalError("Cannot open " PROCSTATFILE); - while (true) { - char buffer[PROC_LINE_LENGTH + 1]; - if (fgets(buffer, sizeof(buffer), statfile) == NULL) - break; - if (String_startsWith(buffer, "btime ") == false) - continue; - if (sscanf(buffer, "btime %lld\n", &btime) == 1) - break; - CRT_fatalError("Failed to parse btime from " PROCSTATFILE); - } - - fclose(statfile); - - if (btime == -1) - CRT_fatalError("No btime in " PROCSTATFILE); - - // Initialize CPU count - LinuxProcessList_updateCPUcount(pl); - - return pl; + return super; } void ProcessList_delete(ProcessList* pl) { LinuxProcessList* this = (LinuxProcessList*) pl; ProcessList_done(pl); - free(this->cpuData); if (this->ttyDrivers) { for (int i = 0; this->ttyDrivers[i].path; i++) { free(this->ttyDrivers[i].path); @@ -364,8 +231,8 @@ void ProcessList_delete(ProcessList* pl) { free(this); } -static inline unsigned long long LinuxProcessList_adjustTime(unsigned long long t) { - return t * 100 / jiffy; +static inline unsigned long long LinuxProcessList_adjustTime(const LinuxMachine* lhost, unsigned long long t) { + return t * 100 / lhost->jiffies; } /* Taken from: https://github.com/torvalds/linux/blob/64570fbc14f8d7cb3fe3995f20e26bc25ce4b2cc/fs/proc/array.c#L120 */ @@ -384,7 +251,7 @@ static inline ProcessState LinuxProcessList_getProcessState(char state) { } } -static bool LinuxProcessList_readStatFile(LinuxProcess* lp, openat_arg_t procFd, bool scanMainThread, char* command, size_t commLen) { +static bool LinuxProcessList_readStatFile(LinuxProcess* lp, openat_arg_t procFd, const LinuxMachine* lhost, bool scanMainThread, char* command, size_t commLen) { Process* process = &lp->super; char buf[MAX_READ + 1]; @@ -457,19 +324,19 @@ static bool LinuxProcessList_readStatFile(LinuxProcess* lp, openat_arg_t procFd, location += 1; /* (14) utime - %lu */ - lp->utime = LinuxProcessList_adjustTime(strtoull(location, &location, 10)); + lp->utime = LinuxProcessList_adjustTime(lhost, strtoull(location, &location, 10)); location += 1; /* (15) stime - %lu */ - lp->stime = LinuxProcessList_adjustTime(strtoull(location, &location, 10)); + lp->stime = LinuxProcessList_adjustTime(lhost, strtoull(location, &location, 10)); location += 1; /* (16) cutime - %ld */ - lp->cutime = LinuxProcessList_adjustTime(strtoull(location, &location, 10)); + lp->cutime = LinuxProcessList_adjustTime(lhost, strtoull(location, &location, 10)); location += 1; /* (17) cstime - %ld */ - lp->cstime = LinuxProcessList_adjustTime(strtoull(location, &location, 10)); + lp->cstime = LinuxProcessList_adjustTime(lhost, strtoull(location, &location, 10)); location += 1; /* (18) priority - %ld */ @@ -489,7 +356,7 @@ static bool LinuxProcessList_readStatFile(LinuxProcess* lp, openat_arg_t procFd, /* (22) starttime - %llu */ if (process->starttime_ctime == 0) { - process->starttime_ctime = btime + LinuxProcessList_adjustTime(strtoll(location, &location, 10)) / 100; + process->starttime_ctime = lhost->boottime + LinuxProcessList_adjustTime(lhost, strtoll(location, &location, 10)) / 100; } else { location = strchr(location, ' '); } @@ -613,8 +480,9 @@ static bool LinuxProcessList_updateUser(const Machine* host, Process* process, o return true; } -static void LinuxProcessList_readIoFile(LinuxProcess* lp, openat_arg_t procFd, bool scanMainThread, unsigned long long realtimeMs) { +static void LinuxProcessList_readIoFile(LinuxProcess* lp, openat_arg_t procFd, bool scanMainThread) { Process* process = &lp->super; + const Machine* host = process->host; char path[20] = "io"; char buffer[1024]; if (scanMainThread) { @@ -631,13 +499,13 @@ static void LinuxProcessList_readIoFile(LinuxProcess* lp, openat_arg_t procFd, b lp->io_read_bytes = ULLONG_MAX; lp->io_write_bytes = ULLONG_MAX; lp->io_cancelled_write_bytes = ULLONG_MAX; - lp->io_last_scan_time_ms = realtimeMs; + lp->io_last_scan_time_ms = host->realtimeMs; return; } unsigned long long last_read = lp->io_read_bytes; unsigned long long last_write = lp->io_write_bytes; - unsigned long long time_delta = realtimeMs > lp->io_last_scan_time_ms ? realtimeMs - lp->io_last_scan_time_ms : 0; + unsigned long long time_delta = host->realtimeMs > lp->io_last_scan_time_ms ? host->realtimeMs - lp->io_last_scan_time_ms : 0; char* buf = buffer; const char* line; @@ -673,7 +541,7 @@ static void LinuxProcessList_readIoFile(LinuxProcess* lp, openat_arg_t procFd, b } } - lp->io_last_scan_time_ms = realtimeMs; + lp->io_last_scan_time_ms = host->realtimeMs; } typedef struct LibraryData_ { @@ -696,7 +564,7 @@ static void LinuxProcessList_calcLibSize_helper(ATTR_UNUSED ht_key_t key, void* *d += v->size; } -static void LinuxProcessList_readMaps(LinuxProcess* process, openat_arg_t procFd, bool calcSize, bool checkDeletedLib) { +static void LinuxProcessList_readMaps(LinuxProcess* process, openat_arg_t procFd, const LinuxMachine* host, bool calcSize, bool checkDeletedLib) { Process* proc = (Process*)process; proc->usesDeletedLib = false; @@ -801,11 +669,11 @@ static void LinuxProcessList_readMaps(LinuxProcess* process, openat_arg_t procFd Hashtable_delete(ht); - process->m_lrs = total_size / pageSize; + process->m_lrs = total_size / host->pageSize; } } -static bool LinuxProcessList_readStatmFile(LinuxProcess* process, openat_arg_t procFd) { +static bool LinuxProcessList_readStatmFile(LinuxProcess* process, openat_arg_t procFd, const LinuxMachine* host) { FILE* statmfile = fopenat(procFd, "statm", "r"); if (!statmfile) return false; @@ -823,8 +691,8 @@ static bool LinuxProcessList_readStatmFile(LinuxProcess* process, openat_arg_t p fclose(statmfile); if (r == 7) { - process->super.m_virt *= pageSizeKB; - process->super.m_resident *= pageSizeKB; + process->super.m_virt *= host->pageSizeKB; + process->super.m_resident *= host->pageSizeKB; } return r == 7; @@ -1422,14 +1290,14 @@ static char* LinuxProcessList_updateTtyDevice(TtyDriver* ttyDrivers, unsigned lo return out; } -static bool isOlderThan(const Machine* host, const Process* proc, unsigned int seconds) { - assert(host->realtimeMs > 0); +static bool isOlderThan(const Process* proc, unsigned int seconds) { + assert(proc->host->realtimeMs > 0); /* Starttime might not yet be parsed */ if (proc->starttime_ctime <= 0) return false; - uint64_t realtime = host->realtimeMs / 1000; + uint64_t realtime = proc->host->realtimeMs / 1000; if (realtime < (uint64_t)proc->starttime_ctime) return false; @@ -1437,9 +1305,9 @@ static bool isOlderThan(const Machine* host, const Process* proc, unsigned int s return realtime - proc->starttime_ctime > seconds; } -static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_t parentFd, const char* dirname, const Process* parent, double period) { +static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_t parentFd, const LinuxMachine* lhost, const char* dirname, const Process* parent) { ProcessList* pl = (ProcessList*) this; - const Machine* host = pl->host; + const Machine* host = &lhost->super; const Settings* settings = host->settings; const ScreenSettings* ss = settings->ss; const struct dirent* entry; @@ -1459,7 +1327,6 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ return false; } - const unsigned int activeCPUs = host->activeCPUs; const bool hideKernelThreads = settings->hideKernelThreads; const bool hideUserlandThreads = settings->hideUserlandThreads; const bool hideRunningInContainer = settings->hideRunningInContainer; @@ -1512,7 +1379,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ proc->tgid = parent ? parent->pid : pid; proc->isUserlandThread = proc->pid != proc->tgid; - LinuxProcessList_recurseProcTree(this, procFd, "task", proc, period); + LinuxProcessList_recurseProcTree(this, procFd, lhost, "task", proc); /* * These conditions will not trigger on first occurrence, cause we need to @@ -1545,16 +1412,16 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ bool scanMainThread = !hideUserlandThreads && !Process_isKernelThread(proc) && !parent; if (ss->flags & PROCESS_FLAG_IO) - LinuxProcessList_readIoFile(lp, procFd, scanMainThread, host->realtimeMs); + LinuxProcessList_readIoFile(lp, procFd, scanMainThread); - if (!LinuxProcessList_readStatmFile(lp, procFd)) + if (!LinuxProcessList_readStatmFile(lp, procFd, lhost)) goto errorReadingProcess; { bool prev = proc->usesDeletedLib; if (!proc->isKernelThread && !proc->isUserlandThread && - ((ss->flags & PROCESS_FLAG_LINUX_LRS_FIX) || (settings->highlightDeletedExe && !proc->procExeDeleted && isOlderThan(host, proc, 10)))) { + ((ss->flags & PROCESS_FLAG_LINUX_LRS_FIX) || (settings->highlightDeletedExe && !proc->procExeDeleted && isOlderThan(proc, 10)))) { // Check if we really should recalculate the M_LRS value for this process uint64_t passedTimeInMs = host->realtimeMs - lp->last_mlrs_calctime; @@ -1563,7 +1430,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ if (passedTimeInMs > recheck) { lp->last_mlrs_calctime = host->realtimeMs; - LinuxProcessList_readMaps(lp, procFd, ss->flags & PROCESS_FLAG_LINUX_LRS_FIX, settings->highlightDeletedExe); + LinuxProcessList_readMaps(lp, procFd, lhost, ss->flags & PROCESS_FLAG_LINUX_LRS_FIX, settings->highlightDeletedExe); } } else { /* Copy from process structure in threads and reset if setting got disabled */ @@ -1593,7 +1460,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ char statCommand[MAX_NAME + 1]; unsigned long long int lasttimes = (lp->utime + lp->stime); unsigned long int tty_nr = proc->tty_nr; - if (!LinuxProcessList_readStatFile(lp, procFd, scanMainThread, statCommand, sizeof(statCommand))) + if (!LinuxProcessList_readStatFile(lp, procFd, lhost, scanMainThread, statCommand, sizeof(statCommand))) goto errorReadingProcess; if (lp->flags & PF_KTHREAD) { @@ -1610,8 +1477,8 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ } /* period might be 0 after system sleep */ - float percent_cpu = (period < 1E-6) ? 0.0F : ((lp->utime + lp->stime - lasttimes) / period * 100.0); - proc->percent_cpu = CLAMP(percent_cpu, 0.0F, activeCPUs * 100.0F); + float percent_cpu = (lhost->period < 1E-6) ? 0.0F : ((lp->utime + lp->stime - lasttimes) / lhost->period * 100.0); + proc->percent_cpu = CLAMP(percent_cpu, 0.0F, host->activeCPUs * 100.0F); proc->percent_mem = proc->m_resident / (double)(host->totalMem) * 100.0; Process_updateCPUFieldWidths(proc->percent_cpu); @@ -1706,7 +1573,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ proc->show = ! ((hideKernelThreads && Process_isKernelThread(proc)) || (hideUserlandThreads && Process_isUserlandThread(proc))); pl->totalTasks++; - /* runningTasks is set in LinuxProcessList_scanCPUTime() from /proc/stat */ + /* runningTasks is set in Machine_scanCPUTime() from /proc/stat */ continue; // Exception handler. @@ -1734,546 +1601,11 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ return true; } -static inline void LinuxProcessList_scanMemoryInfo(ProcessList* this) { - Machine* host = this->host; - LinuxProcessList *lpl = (LinuxProcessList *)this; - memory_t availableMem = 0; - memory_t freeMem = 0; - memory_t totalMem = 0; - memory_t buffersMem = 0; - memory_t cachedMem = 0; - memory_t sharedMem = 0; - memory_t swapTotalMem = 0; - memory_t swapCacheMem = 0; - memory_t swapFreeMem = 0; - memory_t sreclaimableMem = 0; - memory_t zswapCompMem = 0; - memory_t zswapOrigMem = 0; - - FILE* file = fopen(PROCMEMINFOFILE, "r"); - if (!file) - CRT_fatalError("Cannot open " PROCMEMINFOFILE); - - char buffer[128]; - while (fgets(buffer, sizeof(buffer), file)) { - - #define tryRead(label, variable) \ - if (String_startsWith(buffer, label)) { \ - memory_t parsed_; \ - if (sscanf(buffer + strlen(label), "%llu kB", &parsed_) == 1) { \ - (variable) = parsed_; \ - } \ - break; \ - } else (void) 0 /* Require a ";" after the macro use. */ - - switch (buffer[0]) { - case 'M': - tryRead("MemAvailable:", availableMem); - tryRead("MemFree:", freeMem); - tryRead("MemTotal:", totalMem); - break; - case 'B': - tryRead("Buffers:", buffersMem); - break; - case 'C': - tryRead("Cached:", cachedMem); - break; - case 'S': - switch (buffer[1]) { - case 'h': - tryRead("Shmem:", sharedMem); - break; - case 'w': - tryRead("SwapTotal:", swapTotalMem); - tryRead("SwapCached:", swapCacheMem); - tryRead("SwapFree:", swapFreeMem); - break; - case 'R': - tryRead("SReclaimable:", sreclaimableMem); - break; - } - break; - case 'Z': - tryRead("Zswap:", zswapCompMem); - tryRead("Zswapped:", zswapOrigMem); - break; - } - - #undef tryRead - } - - fclose(file); - - /* - * Compute memory partition like procps(free) - * https://gitlab.com/procps-ng/procps/-/blob/master/proc/sysinfo.c - * - * Adjustments: - * - Shmem in part of Cached (see https://lore.kernel.org/patchwork/patch/648763/), - * do not show twice by subtracting from Cached and do not subtract twice from used. - */ - host->totalMem = totalMem; - host->cachedMem = cachedMem + sreclaimableMem - sharedMem; - host->sharedMem = sharedMem; - const memory_t usedDiff = freeMem + cachedMem + sreclaimableMem + buffersMem; - host->usedMem = (totalMem >= usedDiff) ? totalMem - usedDiff : totalMem - freeMem; - host->buffersMem = buffersMem; - host->availableMem = availableMem != 0 ? MINIMUM(availableMem, totalMem) : freeMem; - host->totalSwap = swapTotalMem; - host->usedSwap = swapTotalMem - swapFreeMem - swapCacheMem; - host->cachedSwap = swapCacheMem; - lpl->zswap.usedZswapComp = zswapCompMem; - lpl->zswap.usedZswapOrig = zswapOrigMem; -} - -static void LinuxProcessList_scanHugePages(LinuxProcessList* this) { - this->totalHugePageMem = 0; - for (unsigned i = 0; i < HTOP_HUGEPAGE_COUNT; i++) { - this->usedHugePageMem[i] = MEMORY_MAX; - } - - DIR* dir = opendir("/sys/kernel/mm/hugepages"); - if (!dir) - return; - - const struct dirent* entry; - while ((entry = readdir(dir)) != NULL) { - const char* name = entry->d_name; - - /* Ignore all non-directories */ - if (entry->d_type != DT_DIR && entry->d_type != DT_UNKNOWN) - continue; - - if (!String_startsWith(name, "hugepages-")) - continue; - - char* endptr; - unsigned long int hugePageSize = strtoul(name + strlen("hugepages-"), &endptr, 10); - if (!endptr || *endptr != 'k') - continue; - - char content[64]; - char hugePagePath[128]; - ssize_t r; - - xSnprintf(hugePagePath, sizeof(hugePagePath), "/sys/kernel/mm/hugepages/%s/nr_hugepages", name); - r = xReadfile(hugePagePath, content, sizeof(content)); - if (r <= 0) - continue; - - memory_t total = strtoull(content, NULL, 10); - if (total == 0) - continue; - - xSnprintf(hugePagePath, sizeof(hugePagePath), "/sys/kernel/mm/hugepages/%s/free_hugepages", name); - r = xReadfile(hugePagePath, content, sizeof(content)); - if (r <= 0) - continue; - - memory_t free = strtoull(content, NULL, 10); - - int shift = ffsl(hugePageSize) - 1 - (HTOP_HUGEPAGE_BASE_SHIFT - 10); - assert(shift >= 0 && shift < HTOP_HUGEPAGE_COUNT); - - this->totalHugePageMem += total * hugePageSize; - this->usedHugePageMem[shift] = (total - free) * hugePageSize; - } - - closedir(dir); -} - -static inline void LinuxProcessList_scanZswapInfo(LinuxProcessList *this) { - const Machine* host = this->super.host; - long max_pool_percent = 0; - int r; - char buf[256]; - - r = xReadfile("/sys/module/zswap/parameters/max_pool_percent", buf, 256); - if (r <= 0) { - return; - } - max_pool_percent = strtol(buf, NULL, 10); - if (max_pool_percent < 0 || max_pool_percent > 100) { - return; - } - - this->zswap.totalZswapPool = host->totalMem * max_pool_percent / 100; - /* the rest of the metrics are set in LinuxProcessList_scanMemoryInfo() */ -} - -static inline void LinuxProcessList_scanZramInfo(LinuxProcessList* this) { - memory_t totalZram = 0; - memory_t usedZramComp = 0; - memory_t usedZramOrig = 0; - - char mm_stat[34]; - char disksize[34]; - - unsigned int i = 0; - for (;;) { - xSnprintf(mm_stat, sizeof(mm_stat), "/sys/block/zram%u/mm_stat", i); - xSnprintf(disksize, sizeof(disksize), "/sys/block/zram%u/disksize", i); - i++; - FILE* disksize_file = fopen(disksize, "r"); - FILE* mm_stat_file = fopen(mm_stat, "r"); - if (disksize_file == NULL || mm_stat_file == NULL) { - if (disksize_file) { - fclose(disksize_file); - } - if (mm_stat_file) { - fclose(mm_stat_file); - } - break; - } - memory_t size = 0; - memory_t orig_data_size = 0; - memory_t compr_data_size = 0; - - if (!fscanf(disksize_file, "%llu\n", &size) || - !fscanf(mm_stat_file, " %llu %llu", &orig_data_size, &compr_data_size)) { - fclose(disksize_file); - fclose(mm_stat_file); - break; - } - - totalZram += size; - usedZramComp += compr_data_size; - usedZramOrig += orig_data_size; - - fclose(disksize_file); - fclose(mm_stat_file); - } - - this->zram.totalZram = totalZram / 1024; - this->zram.usedZramComp = usedZramComp / 1024; - this->zram.usedZramOrig = usedZramOrig / 1024; -} - -static inline void LinuxProcessList_scanZfsArcstats(LinuxProcessList* lpl) { - memory_t dbufSize = 0; - memory_t dnodeSize = 0; - memory_t bonusSize = 0; - - FILE* file = fopen(PROCARCSTATSFILE, "r"); - if (file == NULL) { - lpl->zfs.enabled = 0; - return; - } - char buffer[128]; - while (fgets(buffer, 128, file)) { - #define tryRead(label, variable) \ - if (String_startsWith(buffer, label)) { \ - sscanf(buffer + strlen(label), " %*2u %32llu", variable); \ - break; \ - } else (void) 0 /* Require a ";" after the macro use. */ - #define tryReadFlag(label, variable, flag) \ - if (String_startsWith(buffer, label)) { \ - (flag) = sscanf(buffer + strlen(label), " %*2u %32llu", variable); \ - break; \ - } else (void) 0 /* Require a ";" after the macro use. */ - - switch (buffer[0]) { - case 'c': - tryRead("c_min", &lpl->zfs.min); - tryRead("c_max", &lpl->zfs.max); - tryReadFlag("compressed_size", &lpl->zfs.compressed, lpl->zfs.isCompressed); - break; - case 'u': - tryRead("uncompressed_size", &lpl->zfs.uncompressed); - break; - case 's': - tryRead("size", &lpl->zfs.size); - break; - case 'h': - tryRead("hdr_size", &lpl->zfs.header); - break; - case 'd': - tryRead("dbuf_size", &dbufSize); - tryRead("dnode_size", &dnodeSize); - break; - case 'b': - tryRead("bonus_size", &bonusSize); - break; - case 'a': - tryRead("anon_size", &lpl->zfs.anon); - break; - case 'm': - tryRead("mfu_size", &lpl->zfs.MFU); - tryRead("mru_size", &lpl->zfs.MRU); - break; - } - #undef tryRead - #undef tryReadFlag - } - fclose(file); - - lpl->zfs.enabled = (lpl->zfs.size > 0 ? 1 : 0); - lpl->zfs.size /= 1024; - lpl->zfs.min /= 1024; - lpl->zfs.max /= 1024; - lpl->zfs.MFU /= 1024; - lpl->zfs.MRU /= 1024; - lpl->zfs.anon /= 1024; - lpl->zfs.header /= 1024; - lpl->zfs.other = (dbufSize + dnodeSize + bonusSize) / 1024; - if ( lpl->zfs.isCompressed ) { - lpl->zfs.compressed /= 1024; - lpl->zfs.uncompressed /= 1024; - } -} - -static inline double LinuxProcessList_scanCPUTime(ProcessList* super) { +void ProcessList_goThroughEntries(ProcessList* super) { LinuxProcessList* this = (LinuxProcessList*) super; - - LinuxProcessList_updateCPUcount(super); - - FILE* file = fopen(PROCSTATFILE, "r"); - if (!file) - CRT_fatalError("Cannot open " PROCSTATFILE); - - Machine* host = super->host; - unsigned int existingCPUs = host->existingCPUs; - unsigned int lastAdjCpuId = 0; - - for (unsigned int i = 0; i <= existingCPUs; i++) { - char buffer[PROC_LINE_LENGTH + 1]; - unsigned long long int usertime, nicetime, systemtime, idletime; - unsigned long long int ioWait = 0, irq = 0, softIrq = 0, steal = 0, guest = 0, guestnice = 0; - - const char* ok = fgets(buffer, sizeof(buffer), file); - if (!ok) - break; - - // cpu fields are sorted first - if (!String_startsWith(buffer, "cpu")) - break; - - // Depending on your kernel version, - // 5, 7, 8 or 9 of these fields will be set. - // The rest will remain at zero. - unsigned int adjCpuId; - if (i == 0) { - (void) sscanf(buffer, "cpu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu", &usertime, &nicetime, &systemtime, &idletime, &ioWait, &irq, &softIrq, &steal, &guest, &guestnice); - adjCpuId = 0; - } else { - unsigned int cpuid; - (void) sscanf(buffer, "cpu%4u %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu", &cpuid, &usertime, &nicetime, &systemtime, &idletime, &ioWait, &irq, &softIrq, &steal, &guest, &guestnice); - adjCpuId = cpuid + 1; - } - - if (adjCpuId > host->existingCPUs) - break; - - for (unsigned int j = lastAdjCpuId + 1; j < adjCpuId; j++) { - // Skipped an ID, but /proc/stat is ordered => got offline CPU - memset(&(this->cpuData[j]), '\0', sizeof(CPUData)); - } - lastAdjCpuId = adjCpuId; - - // Guest time is already accounted in usertime - usertime -= guest; - nicetime -= guestnice; - // Fields existing on kernels >= 2.6 - // (and RHEL's patched kernel 2.4...) - unsigned long long int idlealltime = idletime + ioWait; - unsigned long long int systemalltime = systemtime + irq + softIrq; - unsigned long long int virtalltime = guest + guestnice; - unsigned long long int totaltime = usertime + nicetime + systemalltime + idlealltime + steal + virtalltime; - CPUData* cpuData = &(this->cpuData[adjCpuId]); - // Since we do a subtraction (usertime - guest) and cputime64_to_clock_t() - // used in /proc/stat rounds down numbers, it can lead to a case where the - // integer overflow. - cpuData->userPeriod = saturatingSub(usertime, cpuData->userTime); - cpuData->nicePeriod = saturatingSub(nicetime, cpuData->niceTime); - cpuData->systemPeriod = saturatingSub(systemtime, cpuData->systemTime); - cpuData->systemAllPeriod = saturatingSub(systemalltime, cpuData->systemAllTime); - cpuData->idleAllPeriod = saturatingSub(idlealltime, cpuData->idleAllTime); - cpuData->idlePeriod = saturatingSub(idletime, cpuData->idleTime); - cpuData->ioWaitPeriod = saturatingSub(ioWait, cpuData->ioWaitTime); - cpuData->irqPeriod = saturatingSub(irq, cpuData->irqTime); - cpuData->softIrqPeriod = saturatingSub(softIrq, cpuData->softIrqTime); - cpuData->stealPeriod = saturatingSub(steal, cpuData->stealTime); - cpuData->guestPeriod = saturatingSub(virtalltime, cpuData->guestTime); - cpuData->totalPeriod = saturatingSub(totaltime, cpuData->totalTime); - cpuData->userTime = usertime; - cpuData->niceTime = nicetime; - cpuData->systemTime = systemtime; - cpuData->systemAllTime = systemalltime; - cpuData->idleAllTime = idlealltime; - cpuData->idleTime = idletime; - cpuData->ioWaitTime = ioWait; - cpuData->irqTime = irq; - cpuData->softIrqTime = softIrq; - cpuData->stealTime = steal; - cpuData->guestTime = virtalltime; - cpuData->totalTime = totaltime; - } - - double period = (double)this->cpuData[0].totalPeriod / host->activeCPUs; - - char buffer[PROC_LINE_LENGTH + 1]; - while (fgets(buffer, sizeof(buffer), file)) { - if (String_startsWith(buffer, "procs_running")) { - super->runningTasks = strtoul(buffer + strlen("procs_running"), NULL, 10); - break; - } - } - - fclose(file); - - return period; -} - -static int scanCPUFrequencyFromSysCPUFreq(LinuxProcessList* this) { - const Machine* host = this->super.host; - unsigned int existingCPUs = host->existingCPUs; - int numCPUsWithFrequency = 0; - unsigned long totalFrequency = 0; - - /* - * On some AMD and Intel CPUs read()ing scaling_cur_freq is quite slow (> 1ms). This delay - * accumulates for every core. For details see issue#471. - * If the read on CPU 0 takes longer than 500us bail out and fall back to reading the - * frequencies from /proc/cpuinfo. - * Once the condition has been met, bail out early for the next couple of scans. - */ - static int timeout = 0; - - if (timeout > 0) { - timeout--; - return -1; - } - - for (unsigned int i = 0; i < existingCPUs; ++i) { - if (!Machine_isCPUonline(host, i)) - continue; - - char pathBuffer[64]; - xSnprintf(pathBuffer, sizeof(pathBuffer), "/sys/devices/system/cpu/cpu%u/cpufreq/scaling_cur_freq", i); - - struct timespec start; - if (i == 0) - clock_gettime(CLOCK_MONOTONIC, &start); - - FILE* file = fopen(pathBuffer, "r"); - if (!file) - return -errno; - - unsigned long frequency; - if (fscanf(file, "%lu", &frequency) == 1) { - /* convert kHz to MHz */ - frequency = frequency / 1000; - this->cpuData[i + 1].frequency = frequency; - numCPUsWithFrequency++; - totalFrequency += frequency; - } - - fclose(file); - - if (i == 0) { - struct timespec end; - clock_gettime(CLOCK_MONOTONIC, &end); - const time_t timeTakenUs = (end.tv_sec - start.tv_sec) * 1000000 + (end.tv_nsec - start.tv_nsec) / 1000; - if (timeTakenUs > 500) { - timeout = 30; - return -1; - } - } - - } - - if (numCPUsWithFrequency > 0) - this->cpuData[0].frequency = (double)totalFrequency / numCPUsWithFrequency; - - return 0; -} - -static void scanCPUFrequencyFromCPUinfo(LinuxProcessList* this) { - FILE* file = fopen(PROCCPUINFOFILE, "r"); - if (file == NULL) - return; - - unsigned int existingCPUs = this->super.host->existingCPUs; - int numCPUsWithFrequency = 0; - double totalFrequency = 0; - int cpuid = -1; - - while (!feof(file)) { - double frequency; - char buffer[PROC_LINE_LENGTH]; - - if (fgets(buffer, PROC_LINE_LENGTH, file) == NULL) - break; - - if (sscanf(buffer, "processor : %d", &cpuid) == 1) { - continue; - } else if ( - (sscanf(buffer, "cpu MHz : %lf", &frequency) == 1) || - (sscanf(buffer, "clock : %lfMHz", &frequency) == 1) - ) { - if (cpuid < 0 || (unsigned int)cpuid > (existingCPUs - 1)) { - continue; - } - - CPUData* cpuData = &(this->cpuData[cpuid + 1]); - /* do not override sysfs data */ - if (isnan(cpuData->frequency)) { - cpuData->frequency = frequency; - } - numCPUsWithFrequency++; - totalFrequency += frequency; - } else if (buffer[0] == '\n') { - cpuid = -1; - } - } - fclose(file); - - if (numCPUsWithFrequency > 0) { - this->cpuData[0].frequency = totalFrequency / numCPUsWithFrequency; - } -} - -static void LinuxProcessList_scanCPUFrequency(LinuxProcessList* this) { - unsigned int existingCPUs = this->super.host->existingCPUs; - - for (unsigned int i = 0; i <= existingCPUs; i++) { - this->cpuData[i].frequency = NAN; - } - - if (scanCPUFrequencyFromSysCPUFreq(this) == 0) { - return; - } - - scanCPUFrequencyFromCPUinfo(this); -} - -void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { - LinuxProcessList* this = (LinuxProcessList*) super; - const Machine* host = super->host; const Settings* settings = host->settings; - - LinuxProcessList_scanMemoryInfo(super); - LinuxProcessList_scanHugePages(this); - LinuxProcessList_scanZfsArcstats(this); - LinuxProcessList_scanZramInfo(this); - LinuxProcessList_scanZswapInfo(this); - - double period = LinuxProcessList_scanCPUTime(super); - - if (settings->showCPUFrequency) { - LinuxProcessList_scanCPUFrequency(this); - } - - #ifdef HAVE_SENSORS_SENSORS_H - if (settings->showCPUTemperature) - LibSensors_getCPUTemperatures(this->cpuData, host->existingCPUs, host->activeCPUs); - #endif - - // in pause mode only gather global data for meters (CPU/memory/...) - if (pauseProcessUpdate) { - return; - } + const LinuxMachine* lhost = (const LinuxMachine*) host; if (settings->ss->flags & PROCESS_FLAG_LINUX_AUTOGROUP) { // Refer to sched(7) 'autogroup feature' section @@ -2293,22 +1625,5 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { openat_arg_t rootFd = ""; #endif - LinuxProcessList_recurseProcTree(this, rootFd, PROCDIR, NULL, period); -} - -Machine* Machine_new(UsersTable* usersTable, uid_t userId) { - Machine* this = xCalloc(1, sizeof(Machine)); - Machine_init(this, usersTable, userId); - return this; -} - -void Machine_delete(Machine* host) { - free(host); -} - -bool Machine_isCPUonline(const Machine* host, unsigned int id) { - assert(id < host->existingCPUs); - - const LinuxProcessList* this = (const LinuxProcessList*) host->pl; - return this->cpuData[id + 1].online; + LinuxProcessList_recurseProcTree(this, rootFd, lhost, PROCDIR, NULL); } diff --git a/linux/LinuxProcessList.h b/linux/LinuxProcessList.h index 69bae5c07..824de482e 100644 --- a/linux/LinuxProcessList.h +++ b/linux/LinuxProcessList.h @@ -15,48 +15,7 @@ in the source distribution for its full text. #include "Hashtable.h" #include "ProcessList.h" #include "UsersTable.h" -#include "linux/ZramStats.h" -#include "linux/ZswapStats.h" -#include "zfs/ZfsArcStats.h" -#define HTOP_HUGEPAGE_BASE_SHIFT 16 -#define HTOP_HUGEPAGE_COUNT 24 - -typedef struct CPUData_ { - unsigned long long int totalTime; - unsigned long long int userTime; - unsigned long long int systemTime; - unsigned long long int systemAllTime; - unsigned long long int idleAllTime; - unsigned long long int idleTime; - unsigned long long int niceTime; - unsigned long long int ioWaitTime; - unsigned long long int irqTime; - unsigned long long int softIrqTime; - unsigned long long int stealTime; - unsigned long long int guestTime; - - unsigned long long int totalPeriod; - unsigned long long int userPeriod; - unsigned long long int systemPeriod; - unsigned long long int systemAllPeriod; - unsigned long long int idleAllPeriod; - unsigned long long int idlePeriod; - unsigned long long int nicePeriod; - unsigned long long int ioWaitPeriod; - unsigned long long int irqPeriod; - unsigned long long int softIrqPeriod; - unsigned long long int stealPeriod; - unsigned long long int guestPeriod; - - double frequency; - - #ifdef HAVE_SENSORS_SENSORS_H - double temperature; - #endif - - bool online; -} CPUData; typedef struct TtyDriver_ { char* path; @@ -68,8 +27,6 @@ typedef struct TtyDriver_ { typedef struct LinuxProcessList_ { ProcessList super; - CPUData* cpuData; - TtyDriver* ttyDrivers; bool haveSmapsRollup; bool haveAutogroup; @@ -78,55 +35,6 @@ typedef struct LinuxProcessList_ { struct nl_sock* netlink_socket; int netlink_family; #endif - - memory_t totalHugePageMem; - memory_t usedHugePageMem[HTOP_HUGEPAGE_COUNT]; - - memory_t availableMem; - - ZfsArcStats zfs; - ZramStats zram; - ZswapStats zswap; } LinuxProcessList; -#ifndef PROCDIR -#define PROCDIR "/proc" -#endif - -#ifndef PROCCPUINFOFILE -#define PROCCPUINFOFILE PROCDIR "/cpuinfo" -#endif - -#ifndef PROCSTATFILE -#define PROCSTATFILE PROCDIR "/stat" -#endif - -#ifndef PROCMEMINFOFILE -#define PROCMEMINFOFILE PROCDIR "/meminfo" -#endif - -#ifndef PROCARCSTATSFILE -#define PROCARCSTATSFILE PROCDIR "/spl/kstat/zfs/arcstats" -#endif - -#ifndef PROCTTYDRIVERSFILE -#define PROCTTYDRIVERSFILE PROCDIR "/tty/drivers" -#endif - -#ifndef PROC_LINE_LENGTH -#define PROC_LINE_LENGTH 4096 -#endif - -ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList); - -void ProcessList_delete(ProcessList* pl); - -void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate); - -Machine* Machine_new(UsersTable* usersTable, uid_t userId); - -bool Machine_isCPUonline(const Machine* host, unsigned int id); - -void Machine_delete(Machine* host); - #endif diff --git a/linux/Platform.c b/linux/Platform.c index 68cefce9b..693044af1 100644 --- a/linux/Platform.c +++ b/linux/Platform.c @@ -36,6 +36,7 @@ in the source distribution for its full text. #include "HostnameMeter.h" #include "HugePageMeter.h" #include "LoadAverageMeter.h" +#include "Machine.h" #include "Macros.h" #include "MainPanel.h" #include "Meter.h" @@ -45,7 +46,6 @@ in the source distribution for its full text. #include "Object.h" #include "Panel.h" #include "PressureStallMeter.h" -#include "ProcessList.h" #include "ProvideCurses.h" #include "linux/SELinuxMeter.h" #include "Settings.h" @@ -56,8 +56,8 @@ in the source distribution for its full text. #include "XUtils.h" #include "linux/IOPriority.h" #include "linux/IOPriorityPanel.h" +#include "linux/LinuxMachine.h" #include "linux/LinuxProcess.h" -#include "linux/LinuxProcessList.h" #include "linux/SystemdMeter.h" #include "linux/ZramMeter.h" #include "linux/ZramStats.h" @@ -304,9 +304,9 @@ int Platform_getMaxPid(void) { } double Platform_setCPUValues(Meter* this, unsigned int cpu) { - const Machine* host = this->host; - const LinuxProcessList* pl = (const LinuxProcessList*) host->pl; - const CPUData* cpuData = &(pl->cpuData[cpu]); + const LinuxMachine* lhost = (const LinuxMachine *) this->host; + const Settings* settings = this->host->settings; + const CPUData* cpuData = &(lhost->cpuData[cpu]); double total = (double) ( cpuData->totalPeriod == 0 ? 1 : cpuData->totalPeriod); double percent; double* v = this->values; @@ -318,7 +318,7 @@ double Platform_setCPUValues(Meter* this, unsigned int cpu) { v[CPU_METER_NICE] = cpuData->nicePeriod / total * 100.0; v[CPU_METER_NORMAL] = cpuData->userPeriod / total * 100.0; - if (host->settings->detailedCPUTime) { + if (settings->detailedCPUTime) { v[CPU_METER_KERNEL] = cpuData->systemPeriod / total * 100.0; v[CPU_METER_IRQ] = cpuData->irqPeriod / total * 100.0; v[CPU_METER_SOFTIRQ] = cpuData->softIrqPeriod / total * 100.0; @@ -327,7 +327,7 @@ double Platform_setCPUValues(Meter* this, unsigned int cpu) { v[CPU_METER_IOWAIT] = cpuData->ioWaitPeriod / total * 100.0; this->curItems = 8; percent = v[CPU_METER_NICE] + v[CPU_METER_NORMAL] + v[CPU_METER_KERNEL] + v[CPU_METER_IRQ] + v[CPU_METER_SOFTIRQ]; - if (host->settings->accountGuestInCPUMeter) { + if (settings->accountGuestInCPUMeter) { percent += v[CPU_METER_STEAL] + v[CPU_METER_GUEST]; } } else { @@ -354,7 +354,7 @@ double Platform_setCPUValues(Meter* this, unsigned int cpu) { void Platform_setMemoryValues(Meter* this) { const Machine* host = this->host; - const LinuxProcessList* lpl = (const LinuxProcessList*) host->pl; + const LinuxMachine* lhost = (const LinuxMachine*) host; this->total = host->totalMem; this->values[MEMORY_METER_USED] = host->usedMem; @@ -364,32 +364,32 @@ void Platform_setMemoryValues(Meter* this) { this->values[MEMORY_METER_CACHE] = host->cachedMem; this->values[MEMORY_METER_AVAILABLE] = host->availableMem; - if (lpl->zfs.enabled != 0 && !Running_containerized) { + if (lhost->zfs.enabled != 0 && !Running_containerized) { // ZFS does not shrink below the value of zfs_arc_min. unsigned long long int shrinkableSize = 0; - if (lpl->zfs.size > lpl->zfs.min) - shrinkableSize = lpl->zfs.size - lpl->zfs.min; + if (lhost->zfs.size > lhost->zfs.min) + shrinkableSize = lhost->zfs.size - lhost->zfs.min; this->values[MEMORY_METER_USED] -= shrinkableSize; this->values[MEMORY_METER_CACHE] += shrinkableSize; this->values[MEMORY_METER_AVAILABLE] += shrinkableSize; } - if (lpl->zswap.usedZswapOrig > 0 || lpl->zswap.usedZswapComp > 0) { - this->values[MEMORY_METER_USED] -= lpl->zswap.usedZswapComp; - this->values[MEMORY_METER_COMPRESSED] += lpl->zswap.usedZswapComp; + if (lhost->zswap.usedZswapOrig > 0 || lhost->zswap.usedZswapComp > 0) { + this->values[MEMORY_METER_USED] -= lhost->zswap.usedZswapComp; + this->values[MEMORY_METER_COMPRESSED] += lhost->zswap.usedZswapComp; } } void Platform_setSwapValues(Meter* this) { const Machine* host = this->host; - const LinuxProcessList* lpl = (const LinuxProcessList*) host->pl; + const LinuxMachine* lhost = (const LinuxMachine*) host; this->total = host->totalSwap; this->values[SWAP_METER_USED] = host->usedSwap; this->values[SWAP_METER_CACHE] = host->cachedSwap; this->values[SWAP_METER_FRONTSWAP] = 0; /* frontswap -- memory that is accounted to swap but resides elsewhere */ - if (lpl->zswap.usedZswapOrig > 0 || lpl->zswap.usedZswapComp > 0) { + if (lhost->zswap.usedZswapOrig > 0 || lhost->zswap.usedZswapComp > 0) { /* * FIXME: Zswapped pages can be both SwapUsed and SwapCached, and we do not know which. * @@ -400,33 +400,34 @@ void Platform_setSwapValues(Meter* this) { * For now, subtract Zswapped from SwapUsed and only if Zswapped > SwapUsed, subtract the * overflow from SwapCached. */ - this->values[SWAP_METER_USED] -= lpl->zswap.usedZswapOrig; + this->values[SWAP_METER_USED] -= lhost->zswap.usedZswapOrig; if (this->values[SWAP_METER_USED] < 0) { /* subtract the overflow from SwapCached */ this->values[SWAP_METER_CACHE] += this->values[SWAP_METER_USED]; this->values[SWAP_METER_USED] = 0; } - this->values[SWAP_METER_FRONTSWAP] += lpl->zswap.usedZswapOrig; + this->values[SWAP_METER_FRONTSWAP] += lhost->zswap.usedZswapOrig; } } void Platform_setZramValues(Meter* this) { - const LinuxProcessList* lpl = (const LinuxProcessList*) this->host->pl; - this->total = lpl->zram.totalZram; - this->values[ZRAM_METER_COMPRESSED] = lpl->zram.usedZramComp; - this->values[ZRAM_METER_UNCOMPRESSED] = lpl->zram.usedZramOrig; + const LinuxMachine* lhost = (const LinuxMachine*) this->host; + + this->total = lhost->zram.totalZram; + this->values[ZRAM_METER_COMPRESSED] = lhost->zram.usedZramComp; + this->values[ZRAM_METER_UNCOMPRESSED] = lhost->zram.usedZramOrig; } void Platform_setZfsArcValues(Meter* this) { - const LinuxProcessList* lpl = (const LinuxProcessList*) this->host->pl; + const LinuxMachine* lhost = (const LinuxMachine*) this->host; - ZfsArcMeter_readStats(this, &(lpl->zfs)); + ZfsArcMeter_readStats(this, &(lhost->zfs)); } void Platform_setZfsCompressedArcValues(Meter* this) { - const LinuxProcessList* lpl = (const LinuxProcessList*) this->host->pl; + const LinuxMachine* lhost = (const LinuxMachine*) this->host; - ZfsCompressedArcMeter_readStats(this, &(lpl->zfs)); + ZfsCompressedArcMeter_readStats(this, &(lhost->zfs)); } char* Platform_getProcessEnv(pid_t pid) { diff --git a/netbsd/NetBSDMachine.c b/netbsd/NetBSDMachine.c new file mode 100644 index 000000000..1e2a0a130 --- /dev/null +++ b/netbsd/NetBSDMachine.c @@ -0,0 +1,282 @@ +/* +htop - NetBSDMachine.c +(C) 2014 Hisham H. Muhammad +(C) 2015 Michael McConville +(C) 2021 Santhosh Raju +(C) 2021 htop dev team +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "netbsd/NetBSDMachine.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "CRT.h" +#include "Machine.h" +#include "Macros.h" +#include "Object.h" +#include "Settings.h" +#include "XUtils.h" + + +static const struct { + const char* name; + long int scale; +} freqSysctls[] = { + { "machdep.est.frequency.current", 1 }, + { "machdep.powernow.frequency.current", 1 }, + { "machdep.intrepid.frequency.current", 1 }, + { "machdep.loongson.frequency.current", 1 }, + { "machdep.cpu.frequency.current", 1 }, + { "machdep.frequency.current", 1 }, + { "machdep.tsc_freq", 1000000 }, +}; + +static void NetBSDMachine_updateCPUcount(NetBSDMachine* this) { + Machine* super = &this->super; + + // Definitions for sysctl(3), cf. https://nxr.netbsd.org/xref/src/sys/sys/sysctl.h#813 + const int mib_ncpu_existing[] = { CTL_HW, HW_NCPU }; // Number of existing CPUs + const int mib_ncpu_online[] = { CTL_HW, HW_NCPUONLINE }; // Number of online/active CPUs + + int r; + unsigned int value; + size_t size; + + bool change = false; + + // Query the number of active/online CPUs. + size = sizeof(value); + r = sysctl(mib_ncpu_online, 2, &value, &size, NULL, 0); + if (r < 0 || value < 1) { + value = 1; + } + + if (value != super->activeCPUs) { + super->activeCPUs = value; + change = true; + } + + // Query the total number of CPUs. + size = sizeof(value); + r = sysctl(mib_ncpu_existing, 2, &value, &size, NULL, 0); + if (r < 0 || value < 1) { + value = super->activeCPUs; + } + + if (value != super->existingCPUs) { + opl->cpuData = xReallocArray(this->cpuData, value + 1, sizeof(CPUData)); + super->existingCPUs = value; + change = true; + } + + // Reset CPU stats when number of online/existing CPU cores changed + if (change) { + CPUData* dAvg = &this->cpuData[0]; + memset(dAvg, '\0', sizeof(CPUData)); + dAvg->totalTime = 1; + dAvg->totalPeriod = 1; + + for (unsigned int i = 0; i < super->existingCPUs; i++) { + CPUData* d = &this->cpuData[i + 1]; + memset(d, '\0', sizeof(CPUData)); + d->totalTime = 1; + d->totalPeriod = 1; + } + } +} + +Machine* Machine_new(UsersTable* usersTable, uid_t userId) { + const int fmib[] = { CTL_KERN, KERN_FSCALE }; + size_t size; + char errbuf[_POSIX2_LINE_MAX]; + + NetBSDMachine* this = xCalloc(1, sizeof(NetBSDMachine)); + Machine* super = &this->super; + Machine_init(super, usersTable, userId); + + NetBSDMachine_updateCPUcount(this); + + size = sizeof(this->fscale); + if (sysctl(fmib, 2, &this->fscale, &size, NULL, 0) < 0) { + CRT_fatalError("fscale sysctl call failed"); + } + + if ((this->pageSize = sysconf(_SC_PAGESIZE)) == -1) + CRT_fatalError("pagesize sysconf call failed"); + this->pageSizeKB = this->pageSize / ONE_K; + + this->kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf); + if (this->kd == NULL) { + CRT_fatalError("kvm_openfiles() failed"); + } + + return this; +} + +void Machine_delete(Machine* super) { + NetBSDMachine* this = (NetBSDProcessList*) super; + + Machine_done(super); + + if (this->kd) { + kvm_close(this->kd); + } + free(this->cpuData); + free(this); +} + +static void NetBSDMachine_scanMemoryInfo(NetBSDMachine* this) { + Machine* super = &this->super; + + static int uvmexp_mib[] = {CTL_VM, VM_UVMEXP2}; + struct uvmexp_sysctl uvmexp; + size_t size_uvmexp = sizeof(uvmexp); + + if (sysctl(uvmexp_mib, 2, &uvmexp, &size_uvmexp, NULL, 0) < 0) { + CRT_fatalError("uvmexp sysctl call failed"); + } + + super->totalMem = uvmexp.npages * this->pageSizeKB; + super->buffersMem = 0; + super->cachedMem = (uvmexp.filepages + uvmexp.execpages) * this->pageSizeKB; + super->usedMem = (uvmexp.active + uvmexp.wired) * this->pageSizeKB; + super->totalSwap = uvmexp.swpages * this->pageSizeKB; + super->usedSwap = uvmexp.swpginuse * this->pageSizeKB; +} + +static void getKernelCPUTimes(int cpuId, u_int64_t* times) { + const int mib[] = { CTL_KERN, KERN_CP_TIME, cpuId }; + size_t length = sizeof(*times) * CPUSTATES; + if (sysctl(mib, 3, times, &length, NULL, 0) == -1 || length != sizeof(*times) * CPUSTATES) { + CRT_fatalError("sysctl kern.cp_time2 failed"); + } +} + +static void kernelCPUTimesToHtop(const u_int64_t* times, CPUData* cpu) { + unsigned long long totalTime = 0; + for (int i = 0; i < CPUSTATES; i++) { + totalTime += times[i]; + } + + unsigned long long sysAllTime = times[CP_INTR] + times[CP_SYS]; + + cpu->totalPeriod = saturatingSub(totalTime, cpu->totalTime); + cpu->userPeriod = saturatingSub(times[CP_USER], cpu->userTime); + cpu->nicePeriod = saturatingSub(times[CP_NICE], cpu->niceTime); + cpu->sysPeriod = saturatingSub(times[CP_SYS], cpu->sysTime); + cpu->sysAllPeriod = saturatingSub(sysAllTime, cpu->sysAllTime); + cpu->intrPeriod = saturatingSub(times[CP_INTR], cpu->intrTime); + cpu->idlePeriod = saturatingSub(times[CP_IDLE], cpu->idleTime); + + cpu->totalTime = totalTime; + cpu->userTime = times[CP_USER]; + cpu->niceTime = times[CP_NICE]; + cpu->sysTime = times[CP_SYS]; + cpu->sysAllTime = sysAllTime; + cpu->intrTime = times[CP_INTR]; + cpu->idleTime = times[CP_IDLE]; +} + +static void NetBSDMachine_scanCPUTime(NetBSDMachine* this) { + const Machine* super = &this->super; + + u_int64_t kernelTimes[CPUSTATES] = {0}; + u_int64_t avg[CPUSTATES] = {0}; + + for (unsigned int i = 0; i < super->existingCPUs; i++) { + getKernelCPUTimes(i, kernelTimes); + CPUData* cpu = &this->cpuData[i + 1]; + kernelCPUTimesToHtop(kernelTimes, cpu); + + avg[CP_USER] += cpu->userTime; + avg[CP_NICE] += cpu->niceTime; + avg[CP_SYS] += cpu->sysTime; + avg[CP_INTR] += cpu->intrTime; + avg[CP_IDLE] += cpu->idleTime; + } + + for (int i = 0; i < CPUSTATES; i++) { + avg[i] /= super->activeCPUs; + } + + kernelCPUTimesToHtop(avg, &this->cpuData[0]); +} + +static void NetBSDMachine_scanCPUFrequency(NetBSDMachine* this) { + const Machine* super = &this->super; + unsigned int cpus = super->existingCPUs; + bool match = false; + char name[64]; + long int freq = 0; + size_t freqSize; + + for (unsigned int i = 0; i < cpus; i++) { + this->cpuData[i + 1].frequency = NAN; + } + + /* newer hardware supports per-core frequency, for e.g. ARM big.LITTLE */ + for (unsigned int i = 0; i < cpus; i++) { + xSnprintf(name, sizeof(name), "machdep.cpufreq.cpu%u.current", i); + freqSize = sizeof(freq); + if (sysctlbyname(name, &freq, &freqSize, NULL, 0) != -1) { + this->cpuData[i + 1].frequency = freq; /* already in MHz */ + match = true; + } + } + + if (match) { + return; + } + + /* + * Iterate through legacy sysctl nodes for single-core frequency until + * we find a match... + */ + for (size_t i = 0; i < ARRAYSIZE(freqSysctls); i++) { + freqSize = sizeof(freq); + if (sysctlbyname(freqSysctls[i].name, &freq, &freqSize, NULL, 0) != -1) { + freq /= freqSysctls[i].scale; /* scale to MHz */ + match = true; + break; + } + } + + if (match) { + for (unsigned int i = 0; i < cpus; i++) { + this->cpuData[i + 1].frequency = freq; + } + } +} + +void Machine_scan(Machine* super) { + NetBSDMachine* this = (NetBSDMachine*) super; + + NetBSDProcessList_scanMemoryInfo(this); + NetBSDProcessList_scanCPUTime(this); + + if (super->settings->showCPUFrequency) { + NetBSDProcessList_scanCPUFrequency(npl); + } +} + +bool Machine_isCPUonline(const Machine* host, unsigned int id) { + assert(id < host->existingCPUs); + + // TODO: Support detecting online / offline CPUs. + return true; +} diff --git a/netbsd/NetBSDMachine.h b/netbsd/NetBSDMachine.h new file mode 100644 index 000000000..57070d675 --- /dev/null +++ b/netbsd/NetBSDMachine.h @@ -0,0 +1,54 @@ +#ifndef HEADER_NetBSDMachine +#define HEADER_NetBSDMachine +/* +htop - NetBSDMachine.h +(C) 2014 Hisham H. Muhammad +(C) 2015 Michael McConville +(C) 2021 Santhosh Raju +(C) 2021 htop dev team +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include +#include +#include + +#include "Machine.h" +#include "ProcessList.h" + + +typedef struct CPUData_ { + unsigned long long int totalTime; + unsigned long long int userTime; + unsigned long long int niceTime; + unsigned long long int sysTime; + unsigned long long int sysAllTime; + unsigned long long int spinTime; + unsigned long long int intrTime; + unsigned long long int idleTime; + + unsigned long long int totalPeriod; + unsigned long long int userPeriod; + unsigned long long int nicePeriod; + unsigned long long int sysPeriod; + unsigned long long int sysAllPeriod; + unsigned long long int spinPeriod; + unsigned long long int intrPeriod; + unsigned long long int idlePeriod; + + double frequency; +} CPUData; + +typedef struct NetBSDProcessList_ { + ProcessList super; + kvm_t* kd; + + long fscale; + int pageSize; + int pageSizeKB; + + CPUData* cpuData; +} NetBSDProcessList; + +#endif diff --git a/netbsd/NetBSDProcessList.c b/netbsd/NetBSDProcessList.c index b7b6915a7..9e5a9be4d 100644 --- a/netbsd/NetBSDProcessList.c +++ b/netbsd/NetBSDProcessList.c @@ -20,7 +20,6 @@ in the source distribution for its full text. #include #include #include -#include #include #include #include @@ -32,137 +31,23 @@ in the source distribution for its full text. #include "ProcessList.h" #include "Settings.h" #include "XUtils.h" +#include "netbsd/NetBSDMachine.h" #include "netbsd/NetBSDProcess.h" -static long fscale; -static int pageSize; -static int pageSizeKB; - -static const struct { - const char* name; - long int scale; -} freqSysctls[] = { - { "machdep.est.frequency.current", 1 }, - { "machdep.powernow.frequency.current", 1 }, - { "machdep.intrepid.frequency.current", 1 }, - { "machdep.loongson.frequency.current", 1 }, - { "machdep.cpu.frequency.current", 1 }, - { "machdep.frequency.current", 1 }, - { "machdep.tsc_freq", 1000000 }, -}; - -static void NetBSDProcessList_updateCPUcount(ProcessList* super) { - NetBSDProcessList* opl = (NetBSDProcessList*) super; - Machine* host = super->host; - - // Definitions for sysctl(3), cf. https://nxr.netbsd.org/xref/src/sys/sys/sysctl.h#813 - const int mib_ncpu_existing[] = { CTL_HW, HW_NCPU }; // Number of existing CPUs - const int mib_ncpu_online[] = { CTL_HW, HW_NCPUONLINE }; // Number of online/active CPUs - - int r; - unsigned int value; - size_t size; - - bool change = false; - - // Query the number of active/online CPUs. - size = sizeof(value); - r = sysctl(mib_ncpu_online, 2, &value, &size, NULL, 0); - if (r < 0 || value < 1) { - value = 1; - } - - if (value != host->activeCPUs) { - host->activeCPUs = value; - change = true; - } - - // Query the total number of CPUs. - size = sizeof(value); - r = sysctl(mib_ncpu_existing, 2, &value, &size, NULL, 0); - if (r < 0 || value < 1) { - value = host->activeCPUs; - } - - if (value != host->existingCPUs) { - opl->cpuData = xReallocArray(opl->cpuData, value + 1, sizeof(CPUData)); - host->existingCPUs = value; - change = true; - } - - // Reset CPU stats when number of online/existing CPU cores changed - if (change) { - CPUData* dAvg = &opl->cpuData[0]; - memset(dAvg, '\0', sizeof(CPUData)); - dAvg->totalTime = 1; - dAvg->totalPeriod = 1; - - for (unsigned int i = 0; i < host->existingCPUs; i++) { - CPUData* d = &opl->cpuData[i + 1]; - memset(d, '\0', sizeof(CPUData)); - d->totalTime = 1; - d->totalPeriod = 1; - } - } -} - ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList) { - const int fmib[] = { CTL_KERN, KERN_FSCALE }; - size_t size; - char errbuf[_POSIX2_LINE_MAX]; - - NetBSDProcessList* npl = xCalloc(1, sizeof(NetBSDProcessList)); - ProcessList* pl = (ProcessList*) npl; - ProcessList_init(pl, Class(NetBSDProcess), host, pidMatchList); + NetBSDProcessList* this = xCalloc(1, sizeof(NetBSDProcessList)); + ProcessList* super = (ProcessList*) this; - NetBSDProcessList_updateCPUcount(pl); - - size = sizeof(fscale); - if (sysctl(fmib, 2, &fscale, &size, NULL, 0) < 0) { - CRT_fatalError("fscale sysctl call failed"); - } + ProcessList_init(super, Class(NetBSDProcess), host, pidMatchList); - if ((pageSize = sysconf(_SC_PAGESIZE)) == -1) - CRT_fatalError("pagesize sysconf call failed"); - pageSizeKB = pageSize / ONE_K; - - npl->kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf); - if (npl->kd == NULL) { - CRT_fatalError("kvm_openfiles() failed"); - } - - return pl; + return super; } void ProcessList_delete(ProcessList* this) { NetBSDProcessList* npl = (NetBSDProcessList*) this; - - if (npl->kd) { - kvm_close(npl->kd); - } - - free(npl->cpuData); - ProcessList_done(this); - free(this); -} - -static void NetBSDProcessList_scanMemoryInfo(Machine* host) { - static int uvmexp_mib[] = {CTL_VM, VM_UVMEXP2}; - struct uvmexp_sysctl uvmexp; - size_t size_uvmexp = sizeof(uvmexp); - - if (sysctl(uvmexp_mib, 2, &uvmexp, &size_uvmexp, NULL, 0) < 0) { - CRT_fatalError("uvmexp sysctl call failed"); - } - - host->totalMem = uvmexp.npages * pageSizeKB; - host->buffersMem = 0; - host->cachedMem = (uvmexp.filepages + uvmexp.execpages) * pageSizeKB; - host->usedMem = (uvmexp.active + uvmexp.wired) * pageSizeKB; - host->totalSwap = uvmexp.swpages * pageSizeKB; - host->usedSwap = uvmexp.swpginuse * pageSizeKB; + free(npl); } static void NetBSDProcessList_updateExe(const struct kinfo_proc2* kproc, Process* proc) { @@ -255,21 +140,22 @@ static void NetBSDProcessList_updateProcessName(kvm_t* kd, const struct kinfo_pr /* * Borrowed with modifications from NetBSD's top(1). */ -static double getpcpu(const struct kinfo_proc2* kp) { - if (fscale == 0) +static double getpcpu(const NetBSDMachine* nhost, const struct kinfo_proc2* kp) { + if (nhost->fscale == 0) return 0.0; - return 100.0 * (double)kp->p_pctcpu / fscale; + return 100.0 * (double)kp->p_pctcpu / nhost->fscale; } static void NetBSDProcessList_scanProcs(NetBSDProcessList* this) { const Machine* host = this->super.host; + const NetBSDMachine* nhost = (const NetBSDMachine*) host; const Settings* settings = host->settings; bool hideKernelThreads = settings->hideKernelThreads; bool hideUserlandThreads = settings->hideUserlandThreads; int count = 0; - const struct kinfo_proc2* kprocs = kvm_getproc2(this->kd, KERN_PROC_ALL, 0, sizeof(struct kinfo_proc2), &count); + const struct kinfo_proc2* kprocs = kvm_getproc2(nhost->kd, KERN_PROC_ALL, 0, sizeof(struct kinfo_proc2), &count); for (int i = 0; i < count; i++) { const struct kinfo_proc2* kproc = &kprocs[i]; @@ -302,10 +188,10 @@ static void NetBSDProcessList_scanProcs(NetBSDProcessList* this) { } NetBSDProcessList_updateExe(kproc, proc); - NetBSDProcessList_updateProcessName(this->kd, kproc, proc); + NetBSDProcessList_updateProcessName(nhost->kd, kproc, proc); } else { if (settings->updateProcessNames) { - NetBSDProcessList_updateProcessName(this->kd, kproc, proc); + NetBSDProcessList_updateProcessName(nhost->kd, kproc, proc); } } @@ -321,8 +207,8 @@ static void NetBSDProcessList_scanProcs(NetBSDProcessList* this) { proc->m_virt = kproc->p_vm_vsize; proc->m_resident = kproc->p_vm_rssize; - proc->percent_mem = (proc->m_resident * pageSizeKB) / (double)(host->totalMem) * 100.0; - proc->percent_cpu = CLAMP(getpcpu(kproc), 0.0, host->activeCPUs * 100.0); + proc->percent_mem = (proc->m_resident * nhost->pageSizeKB) / (double)(host->totalMem) * 100.0; + proc->percent_cpu = CLAMP(getpcpu(nhost, kproc), 0.0, host->activeCPUs * 100.0); Process_updateCPUFieldWidths(proc->percent_cpu); proc->nlwp = kproc->p_nlwps; @@ -334,7 +220,7 @@ static void NetBSDProcessList_scanProcs(NetBSDProcessList* this) { proc->majflt = kproc->p_uru_majflt; int nlwps = 0; - const struct kinfo_lwp* klwps = kvm_getlwps(this->kd, kproc->p_pid, kproc->p_paddr, sizeof(struct kinfo_lwp), &nlwps); + const struct kinfo_lwp* klwps = kvm_getlwps(nhost->kd, kproc->p_pid, kproc->p_paddr, sizeof(struct kinfo_lwp), &nlwps); /* TODO: According to the link below, SDYING should be a regarded state */ /* Taken from: https://ftp.netbsd.org/pub/NetBSD/NetBSD-current/src/sys/sys/proc.h */ @@ -379,140 +265,8 @@ static void NetBSDProcessList_scanProcs(NetBSDProcessList* this) { } } -static void getKernelCPUTimes(int cpuId, u_int64_t* times) { - const int mib[] = { CTL_KERN, KERN_CP_TIME, cpuId }; - size_t length = sizeof(*times) * CPUSTATES; - if (sysctl(mib, 3, times, &length, NULL, 0) == -1 || length != sizeof(*times) * CPUSTATES) { - CRT_fatalError("sysctl kern.cp_time2 failed"); - } -} - -static void kernelCPUTimesToHtop(const u_int64_t* times, CPUData* cpu) { - unsigned long long totalTime = 0; - for (int i = 0; i < CPUSTATES; i++) { - totalTime += times[i]; - } - - unsigned long long sysAllTime = times[CP_INTR] + times[CP_SYS]; - - cpu->totalPeriod = saturatingSub(totalTime, cpu->totalTime); - cpu->userPeriod = saturatingSub(times[CP_USER], cpu->userTime); - cpu->nicePeriod = saturatingSub(times[CP_NICE], cpu->niceTime); - cpu->sysPeriod = saturatingSub(times[CP_SYS], cpu->sysTime); - cpu->sysAllPeriod = saturatingSub(sysAllTime, cpu->sysAllTime); - cpu->intrPeriod = saturatingSub(times[CP_INTR], cpu->intrTime); - cpu->idlePeriod = saturatingSub(times[CP_IDLE], cpu->idleTime); - - cpu->totalTime = totalTime; - cpu->userTime = times[CP_USER]; - cpu->niceTime = times[CP_NICE]; - cpu->sysTime = times[CP_SYS]; - cpu->sysAllTime = sysAllTime; - cpu->intrTime = times[CP_INTR]; - cpu->idleTime = times[CP_IDLE]; -} - -static void NetBSDProcessList_scanCPUTime(NetBSDProcessList* this) { - const Machine* host = this->super.host; - u_int64_t kernelTimes[CPUSTATES] = {0}; - u_int64_t avg[CPUSTATES] = {0}; - - for (unsigned int i = 0; i < host->existingCPUs; i++) { - getKernelCPUTimes(i, kernelTimes); - CPUData* cpu = &this->cpuData[i + 1]; - kernelCPUTimesToHtop(kernelTimes, cpu); - - avg[CP_USER] += cpu->userTime; - avg[CP_NICE] += cpu->niceTime; - avg[CP_SYS] += cpu->sysTime; - avg[CP_INTR] += cpu->intrTime; - avg[CP_IDLE] += cpu->idleTime; - } - - for (int i = 0; i < CPUSTATES; i++) { - avg[i] /= host->activeCPUs; - } - - kernelCPUTimesToHtop(avg, &this->cpuData[0]); -} - -static void NetBSDProcessList_scanCPUFrequency(NetBSDProcessList* this) { - const Machine* host = this->super.host; - unsigned int cpus = host->existingCPUs; - bool match = false; - char name[64]; - long int freq = 0; - size_t freqSize; - - for (unsigned int i = 0; i < cpus; i++) { - this->cpuData[i + 1].frequency = NAN; - } - - /* newer hardware supports per-core frequency, for e.g. ARM big.LITTLE */ - for (unsigned int i = 0; i < cpus; i++) { - xSnprintf(name, sizeof(name), "machdep.cpufreq.cpu%u.current", i); - freqSize = sizeof(freq); - if (sysctlbyname(name, &freq, &freqSize, NULL, 0) != -1) { - this->cpuData[i + 1].frequency = freq; /* already in MHz */ - match = true; - } - } - - if (match) { - return; - } - - /* - * Iterate through legacy sysctl nodes for single-core frequency until - * we find a match... - */ - for (size_t i = 0; i < ARRAYSIZE(freqSysctls); i++) { - freqSize = sizeof(freq); - if (sysctlbyname(freqSysctls[i].name, &freq, &freqSize, NULL, 0) != -1) { - freq /= freqSysctls[i].scale; /* scale to MHz */ - match = true; - break; - } - } - - if (match) { - for (unsigned int i = 0; i < cpus; i++) { - this->cpuData[i + 1].frequency = freq; - } - } -} - -void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { +void ProcessList_goThroughEntries(ProcessList* super) { NetBSDProcessList* npl = (NetBSDProcessList*) super; - NetBSDProcessList_scanMemoryInfo(super->host); - NetBSDProcessList_scanCPUTime(npl); - - if (super->settings->showCPUFrequency) { - NetBSDProcessList_scanCPUFrequency(npl); - } - - // in pause mode only gather global data for meters (CPU/memory/...) - if (pauseProcessUpdate) { - return; - } - NetBSDProcessList_scanProcs(npl); } - -Machine* Machine_new(UsersTable* usersTable, uid_t userId) { - Machine* this = xCalloc(1, sizeof(Machine)); - Machine_init(this, usersTable, userId); - return this; -} - -void Machine_delete(Machine* host) { - free(host); -} - -bool Machine_isCPUonline(const Machine* host, unsigned int id) { - assert(id < host->existingCPUs); - - // TODO: Support detecting online / offline CPUs. - return true; -} diff --git a/netbsd/NetBSDProcessList.h b/netbsd/NetBSDProcessList.h index 0fe18b03f..362d84fc0 100644 --- a/netbsd/NetBSDProcessList.h +++ b/netbsd/NetBSDProcessList.h @@ -10,55 +10,15 @@ Released under the GNU GPLv2+, see the COPYING file in the source distribution for its full text. */ -#include #include #include #include "Hashtable.h" #include "ProcessList.h" -#include "UsersTable.h" -typedef struct CPUData_ { - unsigned long long int totalTime; - unsigned long long int userTime; - unsigned long long int niceTime; - unsigned long long int sysTime; - unsigned long long int sysAllTime; - unsigned long long int spinTime; - unsigned long long int intrTime; - unsigned long long int idleTime; - - unsigned long long int totalPeriod; - unsigned long long int userPeriod; - unsigned long long int nicePeriod; - unsigned long long int sysPeriod; - unsigned long long int sysAllPeriod; - unsigned long long int spinPeriod; - unsigned long long int intrPeriod; - unsigned long long int idlePeriod; - - double frequency; -} CPUData; - typedef struct NetBSDProcessList_ { ProcessList super; - kvm_t* kd; - - CPUData* cpuData; } NetBSDProcessList; - -ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList); - -void ProcessList_delete(ProcessList* this); - -void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate); - -Machine* Machine_new(UsersTable* usersTable, uid_t userId); - -bool Machine_isCPUonline(const Machine* host, unsigned int id); - -void Machine_delete(Machine* host); - #endif diff --git a/netbsd/Platform.c b/netbsd/Platform.c index ea81a26a4..1d6509ff7 100644 --- a/netbsd/Platform.c +++ b/netbsd/Platform.c @@ -45,7 +45,6 @@ in the source distribution for its full text. #include "MemoryMeter.h" #include "MemorySwapMeter.h" #include "Meter.h" -#include "ProcessList.h" #include "Settings.h" #include "SignalsPanel.h" #include "SwapMeter.h" @@ -54,8 +53,8 @@ in the source distribution for its full text. #include "UptimeMeter.h" #include "XUtils.h" #include "generic/fdstat_sysctl.h" +#include "netbsd/NetBSDMachine.h" #include "netbsd/NetBSDProcess.h" -#include "netbsd/NetBSDProcessList.h" /* * The older proplib APIs will be deprecated in NetBSD 10, but we still @@ -238,8 +237,8 @@ int Platform_getMaxPid(void) { double Platform_setCPUValues(Meter* this, int cpu) { const Machine* host = this->host; - const NetBSDProcessList* npl = (const NetBSDProcessList*) host->pl; - const CPUData* cpuData = &npl->cpuData[cpu]; + const NetBSDMachine* nhost = (const NetBSDMachine*) host; + const CPUData* cpuData = &nhost->cpuData[cpu]; double total = cpuData->totalPeriod == 0 ? 1 : cpuData->totalPeriod; double totalPercent; double* v = this->values; diff --git a/openbsd/OpenBSDMachine.c b/openbsd/OpenBSDMachine.c new file mode 100644 index 000000000..e3e30990b --- /dev/null +++ b/openbsd/OpenBSDMachine.c @@ -0,0 +1,286 @@ +/* +htop - OpenBSDMachine.c +(C) 2014 Hisham H. Muhammad +(C) 2015 Michael McConville +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "openbsd/OpenBSDMachine.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "CRT.h" +#include "Macros.h" +#include "Object.h" +#include "Settings.h" +#include "XUtils.h" + + +static void OpenBSDMachine_updateCPUcount(OpenBSDMachine* this) { + Machine* super = &this->super; + const int nmib[] = { CTL_HW, HW_NCPU }; + const int mib[] = { CTL_HW, HW_NCPUONLINE }; + int r; + unsigned int value; + size_t size; + bool change = false; + + size = sizeof(value); + r = sysctl(mib, 2, &value, &size, NULL, 0); + if (r < 0 || value < 1) { + value = 1; + } + + if (value != super->activeCPUs) { + super->activeCPUs = value; + change = true; + } + + size = sizeof(value); + r = sysctl(nmib, 2, &value, &size, NULL, 0); + if (r < 0 || value < 1) { + value = super->activeCPUs; + } + + if (value != super->existingCPUs) { + this->cpuData = xReallocArray(this->cpuData, value + 1, sizeof(CPUData)); + super->existingCPUs = value; + change = true; + } + + if (change) { + CPUData* dAvg = &this->cpuData[0]; + memset(dAvg, '\0', sizeof(CPUData)); + dAvg->totalTime = 1; + dAvg->totalPeriod = 1; + dAvg->online = true; + + for (unsigned int i = 0; i < super->existingCPUs; i++) { + CPUData* d = &this->cpuData[i + 1]; + memset(d, '\0', sizeof(CPUData)); + d->totalTime = 1; + d->totalPeriod = 1; + + const int ncmib[] = { CTL_KERN, KERN_CPUSTATS, i }; + struct cpustats cpu_stats; + + size = sizeof(cpu_stats); + if (sysctl(ncmib, 3, &cpu_stats, &size, NULL, 0) < 0) { + CRT_fatalError("ncmib sysctl call failed"); + } + d->online = (cpu_stats.cs_flags & CPUSTATS_ONLINE); + } + } +} + +Machine* Machine_new(UsersTable* usersTable, uid_t userId) { + const int fmib[] = { CTL_KERN, KERN_FSCALE }; + size_t size; + char errbuf[_POSIX2_LINE_MAX]; + + OpenBSDMachine* this = xCalloc(1, sizeof(OpenBSDMachine)); + Machine* super = &this->super; + + Machine_init(super, usersTable, userId); + + OpenBSDProcessList_updateCPUcount(this); + + size = sizeof(this->fscale); + if (sysctl(fmib, 2, &this->fscale, &size, NULL, 0) < 0) { + CRT_fatalError("fscale sysctl call failed"); + } + + if ((this->pageSize = sysconf(_SC_PAGESIZE)) == -1) + CRT_fatalError("pagesize sysconf call failed"); + this->pageSizeKB = this->pageSize / ONE_K; + + this->kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf); + if (this->kd == NULL) { + CRT_fatalError("kvm_openfiles() failed"); + } + + this->cpuSpeed = -1; + + return this; +} + +void Machine_delete(Machine* super) { + OpenBSDMachine* this = (OpenBSDMachine*) super; + + if (this->kd) { + kvm_close(this->kd); + } + free(this->cpuData); + Machine_done(super); + free(this); +} + +static void OpenBSDMachine_scanMemoryInfo(OpenBSDMachine* this) { + Machine* host = &this->super; + const int uvmexp_mib[] = { CTL_VM, VM_UVMEXP }; + struct uvmexp uvmexp; + size_t size_uvmexp = sizeof(uvmexp); + + if (sysctl(uvmexp_mib, 2, &uvmexp, &size_uvmexp, NULL, 0) < 0) { + CRT_fatalError("uvmexp sysctl call failed"); + } + + super->totalMem = uvmexp.npages * this->pageSizeKB; + super->usedMem = (uvmexp.npages - uvmexp.free - uvmexp.paging) * this->pageSizeKB; + + // Taken from OpenBSD systat/iostat.c, top/machine.c and uvm_sysctl(9) + const int bcache_mib[] = { CTL_VFS, VFS_GENERIC, VFS_BCACHESTAT }; + struct bcachestats bcstats; + size_t size_bcstats = sizeof(bcstats); + + if (sysctl(bcache_mib, 3, &bcstats, &size_bcstats, NULL, 0) < 0) { + CRT_fatalError("cannot get vfs.bcachestat"); + } + + super->cachedMem = bcstats.numbufpages * this->pageSizeKB; + + /* + * Copyright (c) 1994 Thorsten Lockert + * All rights reserved. + * + * Taken almost directly from OpenBSD's top(1) + * + * Originally released under a BSD-3 license + * Modified through htop developers applying GPL-2 + */ + int nswap = swapctl(SWAP_NSWAP, 0, 0); + if (nswap > 0) { + struct swapent swdev[nswap]; + int rnswap = swapctl(SWAP_STATS, swdev, nswap); + + /* Total things up */ + unsigned long long int total = 0, used = 0; + for (int i = 0; i < rnswap; i++) { + if (swdev[i].se_flags & SWF_ENABLE) { + used += (swdev[i].se_inuse / (1024 / DEV_BSIZE)); + total += (swdev[i].se_nblks / (1024 / DEV_BSIZE)); + } + } + + super->totalSwap = total; + super->usedSwap = used; + } else { + super->totalSwap = super->usedSwap = 0; + } +} + +static void getKernelCPUTimes(unsigned int cpuId, u_int64_t* times) { + const int mib[] = { CTL_KERN, KERN_CPTIME2, cpuId }; + size_t length = sizeof(*times) * CPUSTATES; + if (sysctl(mib, 3, times, &length, NULL, 0) == -1 || length != sizeof(*times) * CPUSTATES) { + CRT_fatalError("sysctl kern.cp_time2 failed"); + } +} + +static void kernelCPUTimesToHtop(const u_int64_t* times, CPUData* cpu) { + unsigned long long totalTime = 0; + for (int i = 0; i < CPUSTATES; i++) { + totalTime += times[i]; + } + + unsigned long long sysAllTime = times[CP_INTR] + times[CP_SYS]; + + // XXX Not sure if CP_SPIN should be added to sysAllTime. + // See https://github.com/openbsd/src/commit/531d8034253fb82282f0f353c086e9ad827e031c + #ifdef CP_SPIN + sysAllTime += times[CP_SPIN]; + #endif + + cpu->totalPeriod = saturatingSub(totalTime, cpu->totalTime); + cpu->userPeriod = saturatingSub(times[CP_USER], cpu->userTime); + cpu->nicePeriod = saturatingSub(times[CP_NICE], cpu->niceTime); + cpu->sysPeriod = saturatingSub(times[CP_SYS], cpu->sysTime); + cpu->sysAllPeriod = saturatingSub(sysAllTime, cpu->sysAllTime); + #ifdef CP_SPIN + cpu->spinPeriod = saturatingSub(times[CP_SPIN], cpu->spinTime); + #endif + cpu->intrPeriod = saturatingSub(times[CP_INTR], cpu->intrTime); + cpu->idlePeriod = saturatingSub(times[CP_IDLE], cpu->idleTime); + + cpu->totalTime = totalTime; + cpu->userTime = times[CP_USER]; + cpu->niceTime = times[CP_NICE]; + cpu->sysTime = times[CP_SYS]; + cpu->sysAllTime = sysAllTime; + #ifdef CP_SPIN + cpu->spinTime = times[CP_SPIN]; + #endif + cpu->intrTime = times[CP_INTR]; + cpu->idleTime = times[CP_IDLE]; +} + +static void OpenBSDMachine_scanCPUTime(OpenBSDMachine* this) { + Machine* host = &this->super; + u_int64_t kernelTimes[CPUSTATES] = {0}; + u_int64_t avg[CPUSTATES] = {0}; + + for (unsigned int i = 0; i < super->existingCPUs; i++) { + CPUData* cpu = &this->cpuData[i + 1]; + + if (!cpu->online) { + continue; + } + + getKernelCPUTimes(i, kernelTimes); + kernelCPUTimesToHtop(kernelTimes, cpu); + + avg[CP_USER] += cpu->userTime; + avg[CP_NICE] += cpu->niceTime; + avg[CP_SYS] += cpu->sysTime; + #ifdef CP_SPIN + avg[CP_SPIN] += cpu->spinTime; + #endif + avg[CP_INTR] += cpu->intrTime; + avg[CP_IDLE] += cpu->idleTime; + } + + for (int i = 0; i < CPUSTATES; i++) { + avg[i] /= super->activeCPUs; + } + + kernelCPUTimesToHtop(avg, &this->cpuData[0]); + + { + const int mib[] = { CTL_HW, HW_CPUSPEED }; + int cpuSpeed; + size_t size = sizeof(cpuSpeed); + if (sysctl(mib, 2, &cpuSpeed, &size, NULL, 0) == -1) { + this->cpuSpeed = -1; + } else { + this->cpuSpeed = cpuSpeed; + } + } +} + +void Machine_scan(Machine* super) { + OpenBSDMachine* this = (OpenBSDMachine*) super; + + OpenBSDMachine_updateCPUcount(this); + OpenBSDMachine_scanMemoryInfo(this); + OpenBSDMachine_scanCPUTime(this); +} + +bool Machine_isCPUonline(const Machine* super, unsigned int id) { + assert(id < super->existingCPUs); + + const OpenBSDMachine* this = (const OpenBSDMachine*) super; + return this->cpuData[id + 1].online; +} diff --git a/openbsd/OpenBSDMachine.h b/openbsd/OpenBSDMachine.h new file mode 100644 index 000000000..51d6a7502 --- /dev/null +++ b/openbsd/OpenBSDMachine.h @@ -0,0 +1,53 @@ +#ifndef HEADER_OpenBSDMachine +#define HEADER_OpenBSDMachine +/* +htop - OpenBSDMachine.h +(C) 2014 Hisham H. Muhammad +(C) 2015 Michael McConville +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include +#include +#include + +#include "Machine.h" + + +typedef struct CPUData_ { + unsigned long long int totalTime; + unsigned long long int userTime; + unsigned long long int niceTime; + unsigned long long int sysTime; + unsigned long long int sysAllTime; + unsigned long long int spinTime; + unsigned long long int intrTime; + unsigned long long int idleTime; + + unsigned long long int totalPeriod; + unsigned long long int userPeriod; + unsigned long long int nicePeriod; + unsigned long long int sysPeriod; + unsigned long long int sysAllPeriod; + unsigned long long int spinPeriod; + unsigned long long int intrPeriod; + unsigned long long int idlePeriod; + + bool online; +} CPUData; + +typedef struct OpenBSDMachine_ { + Machine super; + kvm_t* kd; + + CPUData* cpuData; + + long fscale; + int cpuSpeed; + int pageSize; + int pageSizeKB; + +} OpenBSDMachine; + +#endif diff --git a/openbsd/OpenBSDProcessList.c b/openbsd/OpenBSDProcessList.c index c83eb5430..2d86a9d45 100644 --- a/openbsd/OpenBSDProcessList.c +++ b/openbsd/OpenBSDProcessList.c @@ -17,7 +17,6 @@ in the source distribution for its full text. #include #include #include -#include #include #include #include @@ -29,169 +28,26 @@ in the source distribution for its full text. #include "ProcessList.h" #include "Settings.h" #include "XUtils.h" +#include "openbsd/OpenBSDMachine.h" #include "openbsd/OpenBSDProcess.h" -static long fscale; -static int pageSize; -static int pageSizeKB; - -static void OpenBSDProcessList_updateCPUcount(ProcessList* super) { - OpenBSDProcessList* opl = (OpenBSDProcessList*) super; - Machine* host = super->host; - const int nmib[] = { CTL_HW, HW_NCPU }; - const int mib[] = { CTL_HW, HW_NCPUONLINE }; - int r; - unsigned int value; - size_t size; - bool change = false; - - size = sizeof(value); - r = sysctl(mib, 2, &value, &size, NULL, 0); - if (r < 0 || value < 1) { - value = 1; - } - - if (value != host->activeCPUs) { - host->activeCPUs = value; - change = true; - } - - size = sizeof(value); - r = sysctl(nmib, 2, &value, &size, NULL, 0); - if (r < 0 || value < 1) { - value = host->activeCPUs; - } - - if (value != host->existingCPUs) { - opl->cpuData = xReallocArray(opl->cpuData, value + 1, sizeof(CPUData)); - host->existingCPUs = value; - change = true; - } - - if (change) { - CPUData* dAvg = &opl->cpuData[0]; - memset(dAvg, '\0', sizeof(CPUData)); - dAvg->totalTime = 1; - dAvg->totalPeriod = 1; - dAvg->online = true; - - for (unsigned int i = 0; i < host->existingCPUs; i++) { - CPUData* d = &opl->cpuData[i + 1]; - memset(d, '\0', sizeof(CPUData)); - d->totalTime = 1; - d->totalPeriod = 1; - - const int ncmib[] = { CTL_KERN, KERN_CPUSTATS, i }; - struct cpustats cpu_stats; - - size = sizeof(cpu_stats); - if (sysctl(ncmib, 3, &cpu_stats, &size, NULL, 0) < 0) { - CRT_fatalError("ncmib sysctl call failed"); - } - d->online = (cpu_stats.cs_flags & CPUSTATS_ONLINE); - } - } -} - - ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList) { - const int fmib[] = { CTL_KERN, KERN_FSCALE }; - size_t size; - char errbuf[_POSIX2_LINE_MAX]; - - OpenBSDProcessList* opl = xCalloc(1, sizeof(OpenBSDProcessList)); - ProcessList* pl = (ProcessList*) opl; - ProcessList_init(pl, Class(OpenBSDProcess), host, pidMatchList); - - OpenBSDProcessList_updateCPUcount(pl); - - size = sizeof(fscale); - if (sysctl(fmib, 2, &fscale, &size, NULL, 0) < 0) { - CRT_fatalError("fscale sysctl call failed"); - } - - if ((pageSize = sysconf(_SC_PAGESIZE)) == -1) - CRT_fatalError("pagesize sysconf call failed"); - pageSizeKB = pageSize / ONE_K; + OpenBSDProcessList* this = xCalloc(1, sizeof(OpenBSDProcessList)); + ProcessList* super = (ProcessList*) this; - opl->kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf); - if (opl->kd == NULL) { - CRT_fatalError("kvm_openfiles() failed"); - } - - opl->cpuSpeed = -1; + ProcessList_init(super, Class(OpenBSDProcess), host, pidMatchList); - return pl; + return this; } -void ProcessList_delete(ProcessList* this) { - OpenBSDProcessList* opl = (OpenBSDProcessList*) this; - - if (opl->kd) { - kvm_close(opl->kd); - } - - free(opl->cpuData); +void ProcessList_delete(ProcessList* super) { + OpenBSDProcessList* this = (OpenBSDProcessList*) super; - ProcessList_done(this); + ProcessList_done(super); free(this); } -static void OpenBSDProcessList_scanMemoryInfo(ProcessList* pl) { - Machine* host = pl->host; - const int uvmexp_mib[] = { CTL_VM, VM_UVMEXP }; - struct uvmexp uvmexp; - size_t size_uvmexp = sizeof(uvmexp); - - if (sysctl(uvmexp_mib, 2, &uvmexp, &size_uvmexp, NULL, 0) < 0) { - CRT_fatalError("uvmexp sysctl call failed"); - } - - host->totalMem = uvmexp.npages * pageSizeKB; - host->usedMem = (uvmexp.npages - uvmexp.free - uvmexp.paging) * pageSizeKB; - - // Taken from OpenBSD systat/iostat.c, top/machine.c and uvm_sysctl(9) - const int bcache_mib[] = { CTL_VFS, VFS_GENERIC, VFS_BCACHESTAT }; - struct bcachestats bcstats; - size_t size_bcstats = sizeof(bcstats); - - if (sysctl(bcache_mib, 3, &bcstats, &size_bcstats, NULL, 0) < 0) { - CRT_fatalError("cannot get vfs.bcachestat"); - } - - host->cachedMem = bcstats.numbufpages * pageSizeKB; - - /* - * Copyright (c) 1994 Thorsten Lockert - * All rights reserved. - * - * Taken almost directly from OpenBSD's top(1) - * - * Originally released under a BSD-3 license - * Modified through htop developers applying GPL-2 - */ - int nswap = swapctl(SWAP_NSWAP, 0, 0); - if (nswap > 0) { - struct swapent swdev[nswap]; - int rnswap = swapctl(SWAP_STATS, swdev, nswap); - - /* Total things up */ - unsigned long long int total = 0, used = 0; - for (int i = 0; i < rnswap; i++) { - if (swdev[i].se_flags & SWF_ENABLE) { - used += (swdev[i].se_inuse / (1024 / DEV_BSIZE)); - total += (swdev[i].se_nblks / (1024 / DEV_BSIZE)); - } - } - - host->totalSwap = total; - host->usedSwap = used; - } else { - host->totalSwap = host->usedSwap = 0; - } -} - static void OpenBSDProcessList_updateCwd(const struct kinfo_proc* kproc, Process* proc) { const int mib[] = { CTL_KERN, KERN_PROC_CWD, kproc->p_pid }; char buffer[2048]; @@ -264,21 +120,22 @@ static void OpenBSDProcessList_updateProcessName(kvm_t* kd, const struct kinfo_p /* * Taken from OpenBSD's ps(1). */ -static double getpcpu(const struct kinfo_proc* kp) { - if (fscale == 0) +static double getpcpu(const OpenBSDMachine* ohost, const struct kinfo_proc* kp) { + if (ohost->fscale == 0) return 0.0; - return 100.0 * (double)kp->p_pctcpu / fscale; + return 100.0 * (double)kp->p_pctcpu / ohost->fscale; } static void OpenBSDProcessList_scanProcs(OpenBSDProcessList* this) { Machine* host = this->super.host; + OpenBSDMachine* ohost = (OpenBSDMachine*) host; const Settings* settings = host->settings; const bool hideKernelThreads = settings->hideKernelThreads; const bool hideUserlandThreads = settings->hideUserlandThreads; int count = 0; - const struct kinfo_proc* kprocs = kvm_getprocs(this->kd, KERN_PROC_KTHREAD | KERN_PROC_SHOW_THREADS, 0, sizeof(struct kinfo_proc), &count); + const struct kinfo_proc* kprocs = kvm_getprocs(ohost->kd, KERN_PROC_KTHREAD | KERN_PROC_SHOW_THREADS, 0, sizeof(struct kinfo_proc), &count); for (int i = 0; i < count; i++) { const struct kinfo_proc* kproc = &kprocs[i]; @@ -310,7 +167,7 @@ static void OpenBSDProcessList_scanProcs(OpenBSDProcessList* this) { Process_fillStarttimeBuffer(proc); ProcessList_add(&this->super, proc); - OpenBSDProcessList_updateProcessName(this->kd, kproc, proc); + OpenBSDProcessList_updateProcessName(ohost->kd, kproc, proc); if (settings->ss->flags & PROCESS_FLAG_CWD) { OpenBSDProcessList_updateCwd(kproc, proc); @@ -326,16 +183,16 @@ static void OpenBSDProcessList_scanProcs(OpenBSDProcessList* this) { } } else { if (settings->updateProcessNames) { - OpenBSDProcessList_updateProcessName(this->kd, kproc, proc); + OpenBSDProcessList_updateProcessName(ohost->kd, kproc, proc); } } fp->addr = kproc->p_addr; - proc->m_virt = kproc->p_vm_dsize * pageSizeKB; - proc->m_resident = kproc->p_vm_rssize * pageSizeKB; + proc->m_virt = kproc->p_vm_dsize * ohost->pageSizeKB; + proc->m_resident = kproc->p_vm_rssize * ohost->pageSizeKB; proc->percent_mem = proc->m_resident / (float)host->totalMem * 100.0F; - proc->percent_cpu = CLAMP(getpcpu(kproc), 0.0F, host->activeCPUs * 100.0F); + proc->percent_cpu = CLAMP(getpcpu(ohost, kproc), 0.0F, host->activeCPUs * 100.0F); Process_updateCPUFieldWidths(proc->percent_cpu); proc->nice = kproc->p_nice - 20; @@ -379,122 +236,8 @@ static void OpenBSDProcessList_scanProcs(OpenBSDProcessList* this) { } } -static void getKernelCPUTimes(unsigned int cpuId, u_int64_t* times) { - const int mib[] = { CTL_KERN, KERN_CPTIME2, cpuId }; - size_t length = sizeof(*times) * CPUSTATES; - if (sysctl(mib, 3, times, &length, NULL, 0) == -1 || length != sizeof(*times) * CPUSTATES) { - CRT_fatalError("sysctl kern.cp_time2 failed"); - } -} - -static void kernelCPUTimesToHtop(const u_int64_t* times, CPUData* cpu) { - unsigned long long totalTime = 0; - for (int i = 0; i < CPUSTATES; i++) { - totalTime += times[i]; - } - - unsigned long long sysAllTime = times[CP_INTR] + times[CP_SYS]; - - // XXX Not sure if CP_SPIN should be added to sysAllTime. - // See https://github.com/openbsd/src/commit/531d8034253fb82282f0f353c086e9ad827e031c - #ifdef CP_SPIN - sysAllTime += times[CP_SPIN]; - #endif - - cpu->totalPeriod = saturatingSub(totalTime, cpu->totalTime); - cpu->userPeriod = saturatingSub(times[CP_USER], cpu->userTime); - cpu->nicePeriod = saturatingSub(times[CP_NICE], cpu->niceTime); - cpu->sysPeriod = saturatingSub(times[CP_SYS], cpu->sysTime); - cpu->sysAllPeriod = saturatingSub(sysAllTime, cpu->sysAllTime); - #ifdef CP_SPIN - cpu->spinPeriod = saturatingSub(times[CP_SPIN], cpu->spinTime); - #endif - cpu->intrPeriod = saturatingSub(times[CP_INTR], cpu->intrTime); - cpu->idlePeriod = saturatingSub(times[CP_IDLE], cpu->idleTime); - - cpu->totalTime = totalTime; - cpu->userTime = times[CP_USER]; - cpu->niceTime = times[CP_NICE]; - cpu->sysTime = times[CP_SYS]; - cpu->sysAllTime = sysAllTime; - #ifdef CP_SPIN - cpu->spinTime = times[CP_SPIN]; - #endif - cpu->intrTime = times[CP_INTR]; - cpu->idleTime = times[CP_IDLE]; -} - -static void OpenBSDProcessList_scanCPUTime(OpenBSDProcessList* this) { - Machine* host = this->super.host; - u_int64_t kernelTimes[CPUSTATES] = {0}; - u_int64_t avg[CPUSTATES] = {0}; - - for (unsigned int i = 0; i < host->existingCPUs; i++) { - CPUData* cpu = &this->cpuData[i + 1]; - - if (!cpu->online) { - continue; - } - - getKernelCPUTimes(i, kernelTimes); - kernelCPUTimesToHtop(kernelTimes, cpu); - - avg[CP_USER] += cpu->userTime; - avg[CP_NICE] += cpu->niceTime; - avg[CP_SYS] += cpu->sysTime; - #ifdef CP_SPIN - avg[CP_SPIN] += cpu->spinTime; - #endif - avg[CP_INTR] += cpu->intrTime; - avg[CP_IDLE] += cpu->idleTime; - } - - for (int i = 0; i < CPUSTATES; i++) { - avg[i] /= host->activeCPUs; - } - - kernelCPUTimesToHtop(avg, &this->cpuData[0]); - - { - const int mib[] = { CTL_HW, HW_CPUSPEED }; - int cpuSpeed; - size_t size = sizeof(cpuSpeed); - if (sysctl(mib, 2, &cpuSpeed, &size, NULL, 0) == -1) { - this->cpuSpeed = -1; - } else { - this->cpuSpeed = cpuSpeed; - } - } -} - -void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { - OpenBSDProcessList* opl = (OpenBSDProcessList*) super; - - OpenBSDProcessList_updateCPUcount(super); - OpenBSDProcessList_scanMemoryInfo(super); - OpenBSDProcessList_scanCPUTime(opl); - - // in pause mode only gather global data for meters (CPU/memory/...) - if (pauseProcessUpdate) { - return; - } - - OpenBSDProcessList_scanProcs(opl); -} - -Machine* Machine_new(UsersTable* usersTable, uid_t userId) { - Machine* this = xCalloc(1, sizeof(Machine)); - Machine_init(this, usersTable, userId); - return this; -} - -void Machine_delete(Machine* host) { - free(host); -} - -bool Machine_isCPUonline(const Machine* host, unsigned int id) { - assert(id < host->existingCPUs); +void ProcessList_goThroughEntries(ProcessList* super) { + OpenBSDProcessList* this = (OpenBSDProcessList*) super; - const OpenBSDProcessList* opl = (const OpenBSDProcessList*) host->pl; - return opl->cpuData[id + 1].online; + OpenBSDProcessList_scanProcs(this); } diff --git a/openbsd/OpenBSDProcessList.h b/openbsd/OpenBSDProcessList.h index 6ca2ba2c1..8a03fecb8 100644 --- a/openbsd/OpenBSDProcessList.h +++ b/openbsd/OpenBSDProcessList.h @@ -8,57 +8,14 @@ Released under the GNU GPLv2+, see the COPYING file in the source distribution for its full text. */ -#include #include #include -#include "Hashtable.h" #include "ProcessList.h" -#include "UsersTable.h" -typedef struct CPUData_ { - unsigned long long int totalTime; - unsigned long long int userTime; - unsigned long long int niceTime; - unsigned long long int sysTime; - unsigned long long int sysAllTime; - unsigned long long int spinTime; - unsigned long long int intrTime; - unsigned long long int idleTime; - - unsigned long long int totalPeriod; - unsigned long long int userPeriod; - unsigned long long int nicePeriod; - unsigned long long int sysPeriod; - unsigned long long int sysAllPeriod; - unsigned long long int spinPeriod; - unsigned long long int intrPeriod; - unsigned long long int idlePeriod; - - bool online; -} CPUData; - typedef struct OpenBSDProcessList_ { ProcessList super; - kvm_t* kd; - - CPUData* cpuData; - int cpuSpeed; - } OpenBSDProcessList; - -ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList); - -void ProcessList_delete(ProcessList* this); - -void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate); - -Machine* Machine_new(UsersTable* usersTable, uid_t userId); - -bool Machine_isCPUonline(const Machine* host, unsigned int id); - -void Machine_delete(Machine* host); - #endif diff --git a/openbsd/Platform.c b/openbsd/Platform.c index 03c368566..94e71928e 100644 --- a/openbsd/Platform.c +++ b/openbsd/Platform.c @@ -35,7 +35,6 @@ in the source distribution for its full text. #include "MemoryMeter.h" #include "MemorySwapMeter.h" #include "Meter.h" -#include "ProcessList.h" #include "Settings.h" #include "SignalsPanel.h" #include "SwapMeter.h" @@ -43,8 +42,8 @@ in the source distribution for its full text. #include "TasksMeter.h" #include "UptimeMeter.h" #include "XUtils.h" +#include "openbsd/OpenBSDMachine.h" #include "openbsd/OpenBSDProcess.h" -#include "openbsd/OpenBSDProcessList.h" const ScreenDefaults Platform_defaultScreens[] = { @@ -182,8 +181,8 @@ int Platform_getMaxPid(void) { double Platform_setCPUValues(Meter* this, unsigned int cpu) { const Machine* host = this->host; - const OpenBSDProcessList* pl = (const OpenBSDProcessList*) host->pl; - const CPUData* cpuData = &(pl->cpuData[cpu]); + const OpenBSDMachine* ohost = (const OpenBSDMachine*) host; + const CPUData* cpuData = &(ohost->cpuData[cpu]); double total; double totalPercent; double* v = this->values; @@ -217,7 +216,7 @@ double Platform_setCPUValues(Meter* this, unsigned int cpu) { v[CPU_METER_TEMPERATURE] = NAN; - v[CPU_METER_FREQUENCY] = (pl->cpuSpeed != -1) ? pl->cpuSpeed : NAN; + v[CPU_METER_FREQUENCY] = (ohost->cpuSpeed != -1) ? ohost->cpuSpeed : NAN; return totalPercent; } diff --git a/pcp/PCPMachine.c b/pcp/PCPMachine.c new file mode 100644 index 000000000..59e056247 --- /dev/null +++ b/pcp/PCPMachine.c @@ -0,0 +1,332 @@ +/* +htop - PCPProcessList.c +(C) 2014 Hisham H. Muhammad +(C) 2020-2023 htop dev team +(C) 2020-2023 Red Hat, Inc. +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "config.h" // IWYU pragma: keep + +#include "pcp/PCPMachine.h" + +#include +#include +#include +#include +#include +#include + +#include "Macros.h" +#include "Machine.h" +#include "Object.h" +#include "Platform.h" +#include "Settings.h" +#include "XUtils.h" + +#include "pcp/PCPMetric.h" +#include "pcp/PCPProcess.h" + + +static void PCPMachine_updateCPUcount(PCPMachine* this) { + Machine* super = &this->super; + super->activeCPUs = PCPMetric_instanceCount(PCP_PERCPU_SYSTEM); + unsigned int cpus = Platform_getMaxCPU(); + if (cpus == super->existingCPUs) + return; + if (cpus == 0) + cpus = super->activeCPUs; + if (cpus <= 1) + cpus = super->activeCPUs = 1; + super->existingCPUs = cpus; + + free(this->percpu); + free(this->values); + + this->percpu = xCalloc(cpus, sizeof(pmAtomValue*)); + for (unsigned int i = 0; i < cpus; i++) + this->percpu[i] = xCalloc(CPU_METRIC_COUNT, sizeof(pmAtomValue)); + this->values = xCalloc(cpus, sizeof(pmAtomValue)); +} + +static void PCPMachine_updateMemoryInfo(Machine* host) { + unsigned long long int freeMem = 0; + unsigned long long int swapFreeMem = 0; + unsigned long long int sreclaimableMem = 0; + host->totalMem = host->usedMem = host->cachedMem = 0; + host->usedSwap = host->totalSwap = host->sharedMem = 0; + + pmAtomValue value; + if (PCPMetric_values(PCP_MEM_TOTAL, &value, 1, PM_TYPE_U64) != NULL) + host->totalMem = value.ull; + if (PCPMetric_values(PCP_MEM_FREE, &value, 1, PM_TYPE_U64) != NULL) + freeMem = value.ull; + if (PCPMetric_values(PCP_MEM_BUFFERS, &value, 1, PM_TYPE_U64) != NULL) + host->buffersMem = value.ull; + if (PCPMetric_values(PCP_MEM_SRECLAIM, &value, 1, PM_TYPE_U64) != NULL) + sreclaimableMem = value.ull; + if (PCPMetric_values(PCP_MEM_SHARED, &value, 1, PM_TYPE_U64) != NULL) + host->sharedMem = value.ull; + if (PCPMetric_values(PCP_MEM_CACHED, &value, 1, PM_TYPE_U64) != NULL) + host->cachedMem = value.ull + sreclaimableMem - host->sharedMem; + const memory_t usedDiff = freeMem + host->cachedMem + sreclaimableMem + host->buffersMem; + host->usedMem = (host->totalMem >= usedDiff) ? + host->totalMem - usedDiff : host->totalMem - freeMem; + if (PCPMetric_values(PCP_MEM_AVAILABLE, &value, 1, PM_TYPE_U64) != NULL) + host->availableMem = MINIMUM(value.ull, host->totalMem); + else + host->availableMem = freeMem; + if (PCPMetric_values(PCP_MEM_SWAPFREE, &value, 1, PM_TYPE_U64) != NULL) + swapFreeMem = value.ull; + if (PCPMetric_values(PCP_MEM_SWAPTOTAL, &value, 1, PM_TYPE_U64) != NULL) + host->totalSwap = value.ull; + if (PCPMetric_values(PCP_MEM_SWAPCACHED, &value, 1, PM_TYPE_U64) != NULL) + host->cachedSwap = value.ull; + host->usedSwap = host->totalSwap - swapFreeMem - host->cachedSwap; +} + +/* make copies of previously sampled values to avoid overwrite */ +static inline void PCPMachine_backupCPUTime(pmAtomValue* values) { + /* the PERIOD fields (must) mirror the TIME fields */ + for (int metric = CPU_TOTAL_TIME; metric < CPU_TOTAL_PERIOD; metric++) { + values[metric + CPU_TOTAL_PERIOD] = values[metric]; + } +} + +static inline void PCPMachine_saveCPUTimePeriod(pmAtomValue* values, CPUMetric previous, pmAtomValue* latest) { + pmAtomValue* value; + + /* new value for period */ + value = &values[previous]; + if (latest->ull > value->ull) + value->ull = latest->ull - value->ull; + else + value->ull = 0; + + /* new value for time */ + value = &values[previous - CPU_TOTAL_PERIOD]; + value->ull = latest->ull; +} + +/* using copied sampled values and new values, calculate derivations */ +static void PCPMachine_deriveCPUTime(pmAtomValue* values) { + + pmAtomValue* usertime = &values[CPU_USER_TIME]; + pmAtomValue* guesttime = &values[CPU_GUEST_TIME]; + usertime->ull -= guesttime->ull; + + pmAtomValue* nicetime = &values[CPU_NICE_TIME]; + pmAtomValue* guestnicetime = &values[CPU_GUESTNICE_TIME]; + nicetime->ull -= guestnicetime->ull; + + pmAtomValue* idletime = &values[CPU_IDLE_TIME]; + pmAtomValue* iowaittime = &values[CPU_IOWAIT_TIME]; + pmAtomValue* idlealltime = &values[CPU_IDLE_ALL_TIME]; + idlealltime->ull = idletime->ull + iowaittime->ull; + + pmAtomValue* systemtime = &values[CPU_SYSTEM_TIME]; + pmAtomValue* irqtime = &values[CPU_IRQ_TIME]; + pmAtomValue* softirqtime = &values[CPU_SOFTIRQ_TIME]; + pmAtomValue* systalltime = &values[CPU_SYSTEM_ALL_TIME]; + systalltime->ull = systemtime->ull + irqtime->ull + softirqtime->ull; + + pmAtomValue* virtalltime = &values[CPU_GUEST_TIME]; + virtalltime->ull = guesttime->ull + guestnicetime->ull; + + pmAtomValue* stealtime = &values[CPU_STEAL_TIME]; + pmAtomValue* totaltime = &values[CPU_TOTAL_TIME]; + totaltime->ull = usertime->ull + nicetime->ull + systalltime->ull + + idlealltime->ull + stealtime->ull + virtalltime->ull; + + PCPMachine_saveCPUTimePeriod(values, CPU_USER_PERIOD, usertime); + PCPMachine_saveCPUTimePeriod(values, CPU_NICE_PERIOD, nicetime); + PCPMachine_saveCPUTimePeriod(values, CPU_SYSTEM_PERIOD, systemtime); + PCPMachine_saveCPUTimePeriod(values, CPU_SYSTEM_ALL_PERIOD, systalltime); + PCPMachine_saveCPUTimePeriod(values, CPU_IDLE_ALL_PERIOD, idlealltime); + PCPMachine_saveCPUTimePeriod(values, CPU_IDLE_PERIOD, idletime); + PCPMachine_saveCPUTimePeriod(values, CPU_IOWAIT_PERIOD, iowaittime); + PCPMachine_saveCPUTimePeriod(values, CPU_IRQ_PERIOD, irqtime); + PCPMachine_saveCPUTimePeriod(values, CPU_SOFTIRQ_PERIOD, softirqtime); + PCPMachine_saveCPUTimePeriod(values, CPU_STEAL_PERIOD, stealtime); + PCPMachine_saveCPUTimePeriod(values, CPU_GUEST_PERIOD, virtalltime); + PCPMachine_saveCPUTimePeriod(values, CPU_TOTAL_PERIOD, totaltime); +} + +static void PCPMachine_updateAllCPUTime(PCPMachine* this, PCPMetric metric, CPUMetric cpumetric) +{ + pmAtomValue* value = &this->cpu[cpumetric]; + if (PCPMetric_values(metric, value, 1, PM_TYPE_U64) == NULL) + memset(value, 0, sizeof(pmAtomValue)); +} + +static void PCPMachine_updatePerCPUTime(PCPMachine* this, PCPMetric metric, CPUMetric cpumetric) +{ + int cpus = this->super.existingCPUs; + if (PCPMetric_values(metric, this->values, cpus, PM_TYPE_U64) == NULL) + memset(this->values, 0, cpus * sizeof(pmAtomValue)); + for (int i = 0; i < cpus; i++) + this->percpu[i][cpumetric].ull = this->values[i].ull; +} + +static void PCPMachine_updatePerCPUReal(PCPMachine* this, PCPMetric metric, CPUMetric cpumetric) +{ + int cpus = this->super.existingCPUs; + if (PCPMetric_values(metric, this->values, cpus, PM_TYPE_DOUBLE) == NULL) + memset(this->values, 0, cpus * sizeof(pmAtomValue)); + for (int i = 0; i < cpus; i++) + this->percpu[i][cpumetric].d = this->values[i].d; +} + +static inline void PCPMachine_scanZfsArcstats(PCPMachine* this) { + unsigned long long int dbufSize = 0; + unsigned long long int dnodeSize = 0; + unsigned long long int bonusSize = 0; + pmAtomValue value; + + memset(&this->zfs, 0, sizeof(ZfsArcStats)); + if (PCPMetric_values(PCP_ZFS_ARC_ANON_SIZE, &value, 1, PM_TYPE_U64)) + this->zfs.anon = value.ull / ONE_K; + if (PCPMetric_values(PCP_ZFS_ARC_C_MIN, &value, 1, PM_TYPE_U64)) + this->zfs.min = value.ull / ONE_K; + if (PCPMetric_values(PCP_ZFS_ARC_C_MAX, &value, 1, PM_TYPE_U64)) + this->zfs.max = value.ull / ONE_K; + if (PCPMetric_values(PCP_ZFS_ARC_BONUS_SIZE, &value, 1, PM_TYPE_U64)) + bonusSize = value.ull / ONE_K; + if (PCPMetric_values(PCP_ZFS_ARC_DBUF_SIZE, &value, 1, PM_TYPE_U64)) + dbufSize = value.ull / ONE_K; + if (PCPMetric_values(PCP_ZFS_ARC_DNODE_SIZE, &value, 1, PM_TYPE_U64)) + dnodeSize = value.ull / ONE_K; + if (PCPMetric_values(PCP_ZFS_ARC_COMPRESSED_SIZE, &value, 1, PM_TYPE_U64)) + this->zfs.compressed = value.ull / ONE_K; + if (PCPMetric_values(PCP_ZFS_ARC_UNCOMPRESSED_SIZE, &value, 1, PM_TYPE_U64)) + this->zfs.uncompressed = value.ull / ONE_K; + if (PCPMetric_values(PCP_ZFS_ARC_HDR_SIZE, &value, 1, PM_TYPE_U64)) + this->zfs.header = value.ull / ONE_K; + if (PCPMetric_values(PCP_ZFS_ARC_MFU_SIZE, &value, 1, PM_TYPE_U64)) + this->zfs.MFU = value.ull / ONE_K; + if (PCPMetric_values(PCP_ZFS_ARC_MRU_SIZE, &value, 1, PM_TYPE_U64)) + this->zfs.MRU = value.ull / ONE_K; + if (PCPMetric_values(PCP_ZFS_ARC_SIZE, &value, 1, PM_TYPE_U64)) + this->zfs.size = value.ull / ONE_K; + + this->zfs.other = (dbufSize + dnodeSize + bonusSize) / ONE_K; + this->zfs.enabled = (this->zfs.size > 0); + this->zfs.isCompressed = (this->zfs.compressed > 0); +} + +static void PCPMachine_scan(PCPMachine* this) { + Machine* super = &this->super; + + PCPMachine_updateMemoryInfo(super); + PCPMachine_updateCPUcount(this); + + PCPMachine_backupCPUTime(this->cpu); + PCPMachine_updateAllCPUTime(this, PCP_CPU_USER, CPU_USER_TIME); + PCPMachine_updateAllCPUTime(this, PCP_CPU_NICE, CPU_NICE_TIME); + PCPMachine_updateAllCPUTime(this, PCP_CPU_SYSTEM, CPU_SYSTEM_TIME); + PCPMachine_updateAllCPUTime(this, PCP_CPU_IDLE, CPU_IDLE_TIME); + PCPMachine_updateAllCPUTime(this, PCP_CPU_IOWAIT, CPU_IOWAIT_TIME); + PCPMachine_updateAllCPUTime(this, PCP_CPU_IRQ, CPU_IRQ_TIME); + PCPMachine_updateAllCPUTime(this, PCP_CPU_SOFTIRQ, CPU_SOFTIRQ_TIME); + PCPMachine_updateAllCPUTime(this, PCP_CPU_STEAL, CPU_STEAL_TIME); + PCPMachine_updateAllCPUTime(this, PCP_CPU_GUEST, CPU_GUEST_TIME); + PCPMachine_deriveCPUTime(this->cpu); + + for (unsigned int i = 0; i < super->existingCPUs; i++) + PCPMachine_backupCPUTime(this->percpu[i]); + PCPMachine_updatePerCPUTime(this, PCP_PERCPU_USER, CPU_USER_TIME); + PCPMachine_updatePerCPUTime(this, PCP_PERCPU_NICE, CPU_NICE_TIME); + PCPMachine_updatePerCPUTime(this, PCP_PERCPU_SYSTEM, CPU_SYSTEM_TIME); + PCPMachine_updatePerCPUTime(this, PCP_PERCPU_IDLE, CPU_IDLE_TIME); + PCPMachine_updatePerCPUTime(this, PCP_PERCPU_IOWAIT, CPU_IOWAIT_TIME); + PCPMachine_updatePerCPUTime(this, PCP_PERCPU_IRQ, CPU_IRQ_TIME); + PCPMachine_updatePerCPUTime(this, PCP_PERCPU_SOFTIRQ, CPU_SOFTIRQ_TIME); + PCPMachine_updatePerCPUTime(this, PCP_PERCPU_STEAL, CPU_STEAL_TIME); + PCPMachine_updatePerCPUTime(this, PCP_PERCPU_GUEST, CPU_GUEST_TIME); + for (unsigned int i = 0; i < super->existingCPUs; i++) + PCPMachine_deriveCPUTime(this->percpu[i]); + + if (super->settings->showCPUFrequency) + PCPMachine_updatePerCPUReal(this, PCP_HINV_CPUCLOCK, CPU_FREQUENCY); + + PCPMachine_scanZfsArcstats(this); +} + +void Machine_scan(Machine* super) { + PCPMachine* host = (PCPMachine*) super; + const Settings* settings = super->settings; + uint32_t flags = settings->ss->flags; + bool flagged; + + for (int metric = PCP_PROC_PID; metric < PCP_METRIC_COUNT; metric++) + PCPMetric_enable(metric, true); + + flagged = settings->showCPUFrequency; + PCPMetric_enable(PCP_HINV_CPUCLOCK, flagged); + flagged = flags & PROCESS_FLAG_LINUX_CGROUP; + PCPMetric_enable(PCP_PROC_CGROUPS, flagged); + flagged = flags & PROCESS_FLAG_LINUX_OOM; + PCPMetric_enable(PCP_PROC_OOMSCORE, flagged); + flagged = flags & PROCESS_FLAG_LINUX_CTXT; + PCPMetric_enable(PCP_PROC_VCTXSW, flagged); + PCPMetric_enable(PCP_PROC_NVCTXSW, flagged); + flagged = flags & PROCESS_FLAG_LINUX_SECATTR; + PCPMetric_enable(PCP_PROC_LABELS, flagged); + flagged = flags & PROCESS_FLAG_LINUX_AUTOGROUP; + PCPMetric_enable(PCP_PROC_AUTOGROUP_ID, flagged); + PCPMetric_enable(PCP_PROC_AUTOGROUP_NICE, flagged); + + /* Sample smaps metrics on every second pass to improve performance */ + host->smaps_flag = !!host->smaps_flag; + PCPMetric_enable(PCP_PROC_SMAPS_PSS, host->smaps_flag); + PCPMetric_enable(PCP_PROC_SMAPS_SWAP, host->smaps_flag); + PCPMetric_enable(PCP_PROC_SMAPS_SWAPPSS, host->smaps_flag); + + struct timeval timestamp; + if (PCPMetric_fetch(×tamp) != true) + return; + + double sample = host->timestamp; + host->timestamp = pmtimevalToReal(×tamp); + host->period = (host->timestamp - sample) * 100; + + PCPMachine_scan(host); +} + +Machine* Machine_new(UsersTable* usersTable, uid_t userId) { + PCPMachine* this = xCalloc(1, sizeof(PCPMachine)); + Machine* super = &this->super; + + Machine_init(super, usersTable, userId); + + struct timeval timestamp; + gettimeofday(×tamp, NULL); + this->timestamp = pmtimevalToReal(×tamp); + + this->cpu = xCalloc(CPU_METRIC_COUNT, sizeof(pmAtomValue)); + PCPMachine_updateCPUcount(this); + + return super; +} + +void Machine_delete(Machine* super) { + PCPMachine* this = (PCPMachine*) super; + Machine_done(super); + free(this->values); + for (unsigned int i = 0; i < super->existingCPUs; i++) + free(this->percpu[i]); + free(this->percpu); + free(this->cpu); + free(this); +} + +bool Machine_isCPUonline(const Machine* host, unsigned int id) { + assert(id < host->existingCPUs); + (void) host; + + pmAtomValue value; + if (PCPMetric_instance(PCP_PERCPU_SYSTEM, id, id, &value, PM_TYPE_U32)) + return true; + return false; +} diff --git a/pcp/PCPMachine.h b/pcp/PCPMachine.h new file mode 100644 index 000000000..faca8efc9 --- /dev/null +++ b/pcp/PCPMachine.h @@ -0,0 +1,68 @@ +#ifndef HEADER_PCPMachine +#define HEADER_PCPMachine +/* +htop - PCPMachine.h +(C) 2014 Hisham H. Muhammad +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "config.h" // IWYU pragma: keep + +#include +#include + +#include "Hashtable.h" +#include "Machine.h" +#include "UsersTable.h" + +#include "pcp/Platform.h" +#include "zfs/ZfsArcStats.h" + + +typedef enum CPUMetric_ { + CPU_TOTAL_TIME, + CPU_USER_TIME, + CPU_SYSTEM_TIME, + CPU_SYSTEM_ALL_TIME, + CPU_IDLE_ALL_TIME, + CPU_IDLE_TIME, + CPU_NICE_TIME, + CPU_IOWAIT_TIME, + CPU_IRQ_TIME, + CPU_SOFTIRQ_TIME, + CPU_STEAL_TIME, + CPU_GUEST_TIME, + CPU_GUESTNICE_TIME, + + CPU_TOTAL_PERIOD, + CPU_USER_PERIOD, + CPU_SYSTEM_PERIOD, + CPU_SYSTEM_ALL_PERIOD, + CPU_IDLE_ALL_PERIOD, + CPU_IDLE_PERIOD, + CPU_NICE_PERIOD, + CPU_IOWAIT_PERIOD, + CPU_IRQ_PERIOD, + CPU_SOFTIRQ_PERIOD, + CPU_STEAL_PERIOD, + CPU_GUEST_PERIOD, + CPU_GUESTNICE_PERIOD, + + CPU_FREQUENCY, + + CPU_METRIC_COUNT +} CPUMetric; + +typedef struct PCPMachine_ { + Machine super; + int smaps_flag; + double period; + double timestamp; /* previous sample timestamp */ + pmAtomValue* cpu; /* aggregate values for each metric */ + pmAtomValue** percpu; /* per-processor values for each metric */ + pmAtomValue* values; /* per-processor buffer for just one metric */ + ZfsArcStats zfs; +} PCPMachine; + +#endif diff --git a/pcp/PCPProcessList.c b/pcp/PCPProcessList.c index 0e345fc05..c18d74be9 100644 --- a/pcp/PCPProcessList.c +++ b/pcp/PCPProcessList.c @@ -26,68 +26,23 @@ in the source distribution for its full text. #include "Settings.h" #include "XUtils.h" +#include "pcp/PCPMachine.h" #include "pcp/PCPMetric.h" #include "pcp/PCPProcess.h" -static void PCPProcessList_updateCPUcount(PCPProcessList* this) { - Machine* host = this->super.host; - host->activeCPUs = PCPMetric_instanceCount(PCP_PERCPU_SYSTEM); - unsigned int cpus = Platform_getMaxCPU(); - if (cpus == host->existingCPUs) - return; - if (cpus == 0) - cpus = host->activeCPUs; - if (cpus <= 1) - cpus = host->activeCPUs = 1; - host->existingCPUs = cpus; - - free(this->percpu); - free(this->values); - - this->percpu = xCalloc(cpus, sizeof(pmAtomValue*)); - for (unsigned int i = 0; i < cpus; i++) - this->percpu[i] = xCalloc(CPU_METRIC_COUNT, sizeof(pmAtomValue)); - this->values = xCalloc(cpus, sizeof(pmAtomValue)); -} - -static char* setUser(UsersTable* this, unsigned int uid, int pid, int offset) { - char* name = Hashtable_get(this->users, uid); - if (name) - return name; - - pmAtomValue value; - if (PCPMetric_instance(PCP_PROC_ID_USER, pid, offset, &value, PM_TYPE_STRING)) { - Hashtable_put(this->users, uid, value.cp); - name = value.cp; - } - return name; -} - ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList) { PCPProcessList* this = xCalloc(1, sizeof(PCPProcessList)); ProcessList* super = &(this->super); ProcessList_init(super, Class(PCPProcess), host, pidMatchList); - struct timeval timestamp; - gettimeofday(×tamp, NULL); - this->timestamp = pmtimevalToReal(×tamp); - - this->cpu = xCalloc(CPU_METRIC_COUNT, sizeof(pmAtomValue)); - PCPProcessList_updateCPUcount(this); - return super; } -void ProcessList_delete(ProcessList* pl) { - PCPProcessList* this = (PCPProcessList*) pl; - ProcessList_done(pl); - free(this->values); - for (unsigned int i = 0; i < pl->host->existingCPUs; i++) - free(this->percpu[i]); - free(this->percpu); - free(this->cpu); +void ProcessList_delete(ProcessList* super) { + PCPProcessList* this = (PCPProcessList*) super; + ProcessList_done(super); free(this); } @@ -143,6 +98,19 @@ static inline char Metric_instance_char(int metric, int pid, int offset, char fa return fallback; } +static char* setUser(UsersTable* this, unsigned int uid, int pid, int offset) { + char* name = Hashtable_get(this->users, uid); + if (name) + return name; + + pmAtomValue value; + if (PCPMetric_instance(PCP_PROC_ID_USER, pid, offset, &value, PM_TYPE_STRING)) { + Hashtable_put(this->users, uid, value.cp); + name = value.cp; + } + return name; +} + static inline ProcessState PCPProcessList_getProcessState(char state) { switch (state) { case '?': return UNKNOWN; @@ -340,16 +308,17 @@ static void PCPProcessList_updateCmdline(Process* process, int pid, int offset, } } -static bool PCPProcessList_updateProcesses(PCPProcessList* this, double period, struct timeval* tv) { +static bool PCPProcessList_updateProcesses(PCPProcessList* this) { ProcessList* pl = (ProcessList*) this; - Machine* host = pl->host; - const Settings* settings = host->settings; + PCPMachine* phost = (PCPMachine*) host; + const Settings* settings = host->settings; bool hideKernelThreads = settings->hideKernelThreads; bool hideUserlandThreads = settings->hideUserlandThreads; + uint32_t flags = settings->ss->flags; - unsigned long long now = tv->tv_sec * 1000LL + tv->tv_usec / 1000LL; + unsigned long long now = (unsigned long long)(phost->timestamp * 1000); int pid = -1, offset = -1; /* for every process ... */ @@ -387,12 +356,12 @@ static bool PCPProcessList_updateProcesses(PCPProcessList* this, double period, continue; } - if (settings->ss->flags & PROCESS_FLAG_IO) + if (flags & PROCESS_FLAG_IO) PCPProcessList_updateIO(pp, pid, offset, now); PCPProcessList_updateMemory(pp, pid, offset); - if ((settings->ss->flags & PROCESS_FLAG_LINUX_SMAPS) && + if ((flags & PROCESS_FLAG_LINUX_SMAPS) && (Process_isKernelThread(proc) == false)) { if (PCPMetric_enabled(PCP_PROC_SMAPS_PSS)) PCPProcessList_updateSmaps(pp, pid, offset); @@ -407,10 +376,10 @@ static bool PCPProcessList_updateProcesses(PCPProcessList* this, double period, if (tty_nr != proc->tty_nr) PCPProcessList_updateTTY(proc, pid, offset); - float percent_cpu = (pp->utime + pp->stime - lasttimes) / period * 100.0; + float percent_cpu = (pp->utime + pp->stime - lasttimes) / phost->period * 100.0; proc->percent_cpu = isnan(percent_cpu) ? 0.0 : CLAMP(percent_cpu, 0.0, host->activeCPUs * 100.0); - proc->percent_mem = proc->m_resident / (double)host->totalMem * 100.0; + proc->percent_mem = proc->m_resident / (double) host->totalMem * 100.0; Process_updateCPUFieldWidths(proc->percent_cpu); PCPProcessList_updateUsername(proc, pid, offset, host->usersTable); @@ -423,22 +392,22 @@ static bool PCPProcessList_updateProcesses(PCPProcessList* this, double period, PCPProcessList_updateCmdline(proc, pid, offset, command); } - if (settings->ss->flags & PROCESS_FLAG_LINUX_CGROUP) + if (flags & PROCESS_FLAG_LINUX_CGROUP) PCPProcessList_readCGroups(pp, pid, offset); - if (settings->ss->flags & PROCESS_FLAG_LINUX_OOM) + if (flags & PROCESS_FLAG_LINUX_OOM) PCPProcessList_readOomData(pp, pid, offset); - if (settings->ss->flags & PROCESS_FLAG_LINUX_CTXT) + if (flags & PROCESS_FLAG_LINUX_CTXT) PCPProcessList_readCtxtData(pp, pid, offset); - if (settings->ss->flags & PROCESS_FLAG_LINUX_SECATTR) + if (flags & PROCESS_FLAG_LINUX_SECATTR) PCPProcessList_readSecattrData(pp, pid, offset); - if (settings->ss->flags & PROCESS_FLAG_CWD) + if (flags & PROCESS_FLAG_CWD) PCPProcessList_readCwd(pp, pid, offset); - if (settings->ss->flags & PROCESS_FLAG_LINUX_AUTOGROUP) + if (flags & PROCESS_FLAG_LINUX_AUTOGROUP) PCPProcessList_readAutogroup(pp, pid, offset); if (proc->state == ZOMBIE && !proc->cmdline && command[0]) { @@ -467,277 +436,7 @@ static bool PCPProcessList_updateProcesses(PCPProcessList* this, double period, return true; } -static void PCPProcessList_updateMemoryInfo(ProcessList* super) { - Machine* host = super->host; - unsigned long long int freeMem = 0; - unsigned long long int swapFreeMem = 0; - unsigned long long int sreclaimableMem = 0; - host->totalMem = host->usedMem = host->cachedMem = 0; - host->usedSwap = host->totalSwap = host->sharedMem = 0; - - pmAtomValue value; - if (PCPMetric_values(PCP_MEM_TOTAL, &value, 1, PM_TYPE_U64) != NULL) - host->totalMem = value.ull; - if (PCPMetric_values(PCP_MEM_FREE, &value, 1, PM_TYPE_U64) != NULL) - freeMem = value.ull; - if (PCPMetric_values(PCP_MEM_BUFFERS, &value, 1, PM_TYPE_U64) != NULL) - host->buffersMem = value.ull; - if (PCPMetric_values(PCP_MEM_SRECLAIM, &value, 1, PM_TYPE_U64) != NULL) - sreclaimableMem = value.ull; - if (PCPMetric_values(PCP_MEM_SHARED, &value, 1, PM_TYPE_U64) != NULL) - host->sharedMem = value.ull; - if (PCPMetric_values(PCP_MEM_CACHED, &value, 1, PM_TYPE_U64) != NULL) - host->cachedMem = value.ull + sreclaimableMem - host->sharedMem; - const memory_t usedDiff = freeMem + host->cachedMem + sreclaimableMem + host->buffersMem; - host->usedMem = (host->totalMem >= usedDiff) ? - host->totalMem - usedDiff : host->totalMem - freeMem; - if (PCPMetric_values(PCP_MEM_AVAILABLE, &value, 1, PM_TYPE_U64) != NULL) - host->availableMem = MINIMUM(value.ull, host->totalMem); - else - host->availableMem = freeMem; - if (PCPMetric_values(PCP_MEM_SWAPFREE, &value, 1, PM_TYPE_U64) != NULL) - swapFreeMem = value.ull; - if (PCPMetric_values(PCP_MEM_SWAPTOTAL, &value, 1, PM_TYPE_U64) != NULL) - host->totalSwap = value.ull; - if (PCPMetric_values(PCP_MEM_SWAPCACHED, &value, 1, PM_TYPE_U64) != NULL) - host->cachedSwap = value.ull; - host->usedSwap = host->totalSwap - swapFreeMem - host->cachedSwap; -} - -/* make copies of previously sampled values to avoid overwrite */ -static inline void PCPProcessList_backupCPUTime(pmAtomValue* values) { - /* the PERIOD fields (must) mirror the TIME fields */ - for (int metric = CPU_TOTAL_TIME; metric < CPU_TOTAL_PERIOD; metric++) { - values[metric + CPU_TOTAL_PERIOD] = values[metric]; - } -} - -static inline void PCPProcessList_saveCPUTimePeriod(pmAtomValue* values, CPUMetric previous, pmAtomValue* latest) { - pmAtomValue* value; - - /* new value for period */ - value = &values[previous]; - if (latest->ull > value->ull) - value->ull = latest->ull - value->ull; - else - value->ull = 0; - - /* new value for time */ - value = &values[previous - CPU_TOTAL_PERIOD]; - value->ull = latest->ull; -} - -/* using copied sampled values and new values, calculate derivations */ -static void PCPProcessList_deriveCPUTime(pmAtomValue* values) { - - pmAtomValue* usertime = &values[CPU_USER_TIME]; - pmAtomValue* guesttime = &values[CPU_GUEST_TIME]; - usertime->ull -= guesttime->ull; - - pmAtomValue* nicetime = &values[CPU_NICE_TIME]; - pmAtomValue* guestnicetime = &values[CPU_GUESTNICE_TIME]; - nicetime->ull -= guestnicetime->ull; - - pmAtomValue* idletime = &values[CPU_IDLE_TIME]; - pmAtomValue* iowaittime = &values[CPU_IOWAIT_TIME]; - pmAtomValue* idlealltime = &values[CPU_IDLE_ALL_TIME]; - idlealltime->ull = idletime->ull + iowaittime->ull; - - pmAtomValue* systemtime = &values[CPU_SYSTEM_TIME]; - pmAtomValue* irqtime = &values[CPU_IRQ_TIME]; - pmAtomValue* softirqtime = &values[CPU_SOFTIRQ_TIME]; - pmAtomValue* systalltime = &values[CPU_SYSTEM_ALL_TIME]; - systalltime->ull = systemtime->ull + irqtime->ull + softirqtime->ull; - - pmAtomValue* virtalltime = &values[CPU_GUEST_TIME]; - virtalltime->ull = guesttime->ull + guestnicetime->ull; - - pmAtomValue* stealtime = &values[CPU_STEAL_TIME]; - pmAtomValue* totaltime = &values[CPU_TOTAL_TIME]; - totaltime->ull = usertime->ull + nicetime->ull + systalltime->ull + - idlealltime->ull + stealtime->ull + virtalltime->ull; - - PCPProcessList_saveCPUTimePeriod(values, CPU_USER_PERIOD, usertime); - PCPProcessList_saveCPUTimePeriod(values, CPU_NICE_PERIOD, nicetime); - PCPProcessList_saveCPUTimePeriod(values, CPU_SYSTEM_PERIOD, systemtime); - PCPProcessList_saveCPUTimePeriod(values, CPU_SYSTEM_ALL_PERIOD, systalltime); - PCPProcessList_saveCPUTimePeriod(values, CPU_IDLE_ALL_PERIOD, idlealltime); - PCPProcessList_saveCPUTimePeriod(values, CPU_IDLE_PERIOD, idletime); - PCPProcessList_saveCPUTimePeriod(values, CPU_IOWAIT_PERIOD, iowaittime); - PCPProcessList_saveCPUTimePeriod(values, CPU_IRQ_PERIOD, irqtime); - PCPProcessList_saveCPUTimePeriod(values, CPU_SOFTIRQ_PERIOD, softirqtime); - PCPProcessList_saveCPUTimePeriod(values, CPU_STEAL_PERIOD, stealtime); - PCPProcessList_saveCPUTimePeriod(values, CPU_GUEST_PERIOD, virtalltime); - PCPProcessList_saveCPUTimePeriod(values, CPU_TOTAL_PERIOD, totaltime); -} - -static void PCPProcessList_updateAllCPUTime(PCPProcessList* this, PCPMetric metric, CPUMetric cpumetric) -{ - pmAtomValue* value = &this->cpu[cpumetric]; - if (PCPMetric_values(metric, value, 1, PM_TYPE_U64) == NULL) - memset(value, 0, sizeof(pmAtomValue)); -} - -static void PCPProcessList_updatePerCPUTime(PCPProcessList* this, PCPMetric metric, CPUMetric cpumetric) -{ - int cpus = this->super.host->existingCPUs; - if (PCPMetric_values(metric, this->values, cpus, PM_TYPE_U64) == NULL) - memset(this->values, 0, cpus * sizeof(pmAtomValue)); - for (int i = 0; i < cpus; i++) - this->percpu[i][cpumetric].ull = this->values[i].ull; -} - -static void PCPProcessList_updatePerCPUReal(PCPProcessList* this, PCPMetric metric, CPUMetric cpumetric) -{ - int cpus = this->super.host->existingCPUs; - if (PCPMetric_values(metric, this->values, cpus, PM_TYPE_DOUBLE) == NULL) - memset(this->values, 0, cpus * sizeof(pmAtomValue)); - for (int i = 0; i < cpus; i++) - this->percpu[i][cpumetric].d = this->values[i].d; -} - -static inline void PCPProcessList_scanZfsArcstats(PCPProcessList* this) { - unsigned long long int dbufSize = 0; - unsigned long long int dnodeSize = 0; - unsigned long long int bonusSize = 0; - pmAtomValue value; - - memset(&this->zfs, 0, sizeof(ZfsArcStats)); - if (PCPMetric_values(PCP_ZFS_ARC_ANON_SIZE, &value, 1, PM_TYPE_U64)) - this->zfs.anon = value.ull / ONE_K; - if (PCPMetric_values(PCP_ZFS_ARC_C_MIN, &value, 1, PM_TYPE_U64)) - this->zfs.min = value.ull / ONE_K; - if (PCPMetric_values(PCP_ZFS_ARC_C_MAX, &value, 1, PM_TYPE_U64)) - this->zfs.max = value.ull / ONE_K; - if (PCPMetric_values(PCP_ZFS_ARC_BONUS_SIZE, &value, 1, PM_TYPE_U64)) - bonusSize = value.ull / ONE_K; - if (PCPMetric_values(PCP_ZFS_ARC_DBUF_SIZE, &value, 1, PM_TYPE_U64)) - dbufSize = value.ull / ONE_K; - if (PCPMetric_values(PCP_ZFS_ARC_DNODE_SIZE, &value, 1, PM_TYPE_U64)) - dnodeSize = value.ull / ONE_K; - if (PCPMetric_values(PCP_ZFS_ARC_COMPRESSED_SIZE, &value, 1, PM_TYPE_U64)) - this->zfs.compressed = value.ull / ONE_K; - if (PCPMetric_values(PCP_ZFS_ARC_UNCOMPRESSED_SIZE, &value, 1, PM_TYPE_U64)) - this->zfs.uncompressed = value.ull / ONE_K; - if (PCPMetric_values(PCP_ZFS_ARC_HDR_SIZE, &value, 1, PM_TYPE_U64)) - this->zfs.header = value.ull / ONE_K; - if (PCPMetric_values(PCP_ZFS_ARC_MFU_SIZE, &value, 1, PM_TYPE_U64)) - this->zfs.MFU = value.ull / ONE_K; - if (PCPMetric_values(PCP_ZFS_ARC_MRU_SIZE, &value, 1, PM_TYPE_U64)) - this->zfs.MRU = value.ull / ONE_K; - if (PCPMetric_values(PCP_ZFS_ARC_SIZE, &value, 1, PM_TYPE_U64)) - this->zfs.size = value.ull / ONE_K; - - this->zfs.other = (dbufSize + dnodeSize + bonusSize) / ONE_K; - this->zfs.enabled = (this->zfs.size > 0); - this->zfs.isCompressed = (this->zfs.compressed > 0); -} - -static void PCPProcessList_updateHeader(ProcessList* super, const Settings* settings) { - Machine* host = super->host; - PCPProcessList_updateMemoryInfo(super); - +void ProcessList_goThroughEntries(ProcessList* super) { PCPProcessList* this = (PCPProcessList*) super; - PCPProcessList_updateCPUcount(this); - - PCPProcessList_backupCPUTime(this->cpu); - PCPProcessList_updateAllCPUTime(this, PCP_CPU_USER, CPU_USER_TIME); - PCPProcessList_updateAllCPUTime(this, PCP_CPU_NICE, CPU_NICE_TIME); - PCPProcessList_updateAllCPUTime(this, PCP_CPU_SYSTEM, CPU_SYSTEM_TIME); - PCPProcessList_updateAllCPUTime(this, PCP_CPU_IDLE, CPU_IDLE_TIME); - PCPProcessList_updateAllCPUTime(this, PCP_CPU_IOWAIT, CPU_IOWAIT_TIME); - PCPProcessList_updateAllCPUTime(this, PCP_CPU_IRQ, CPU_IRQ_TIME); - PCPProcessList_updateAllCPUTime(this, PCP_CPU_SOFTIRQ, CPU_SOFTIRQ_TIME); - PCPProcessList_updateAllCPUTime(this, PCP_CPU_STEAL, CPU_STEAL_TIME); - PCPProcessList_updateAllCPUTime(this, PCP_CPU_GUEST, CPU_GUEST_TIME); - PCPProcessList_deriveCPUTime(this->cpu); - - for (unsigned int i = 0; i < host->existingCPUs; i++) - PCPProcessList_backupCPUTime(this->percpu[i]); - PCPProcessList_updatePerCPUTime(this, PCP_PERCPU_USER, CPU_USER_TIME); - PCPProcessList_updatePerCPUTime(this, PCP_PERCPU_NICE, CPU_NICE_TIME); - PCPProcessList_updatePerCPUTime(this, PCP_PERCPU_SYSTEM, CPU_SYSTEM_TIME); - PCPProcessList_updatePerCPUTime(this, PCP_PERCPU_IDLE, CPU_IDLE_TIME); - PCPProcessList_updatePerCPUTime(this, PCP_PERCPU_IOWAIT, CPU_IOWAIT_TIME); - PCPProcessList_updatePerCPUTime(this, PCP_PERCPU_IRQ, CPU_IRQ_TIME); - PCPProcessList_updatePerCPUTime(this, PCP_PERCPU_SOFTIRQ, CPU_SOFTIRQ_TIME); - PCPProcessList_updatePerCPUTime(this, PCP_PERCPU_STEAL, CPU_STEAL_TIME); - PCPProcessList_updatePerCPUTime(this, PCP_PERCPU_GUEST, CPU_GUEST_TIME); - for (unsigned int i = 0; i < host->existingCPUs; i++) - PCPProcessList_deriveCPUTime(this->percpu[i]); - - if (settings->showCPUFrequency) - PCPProcessList_updatePerCPUReal(this, PCP_HINV_CPUCLOCK, CPU_FREQUENCY); - - PCPProcessList_scanZfsArcstats(this); -} - -void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { - PCPProcessList* this = (PCPProcessList*) super; - Machine* host = super->host; - const Settings* settings = host->settings; - bool enabled = !pauseProcessUpdate; - - bool flagged = settings->showCPUFrequency; - PCPMetric_enable(PCP_HINV_CPUCLOCK, flagged); - - /* In pause mode do not sample per-process metric values at all */ - for (int metric = PCP_PROC_PID; metric < PCP_METRIC_COUNT; metric++) - PCPMetric_enable(metric, enabled); - - flagged = settings->ss->flags & PROCESS_FLAG_LINUX_CGROUP; - PCPMetric_enable(PCP_PROC_CGROUPS, flagged && enabled); - flagged = settings->ss->flags & PROCESS_FLAG_LINUX_OOM; - PCPMetric_enable(PCP_PROC_OOMSCORE, flagged && enabled); - flagged = settings->ss->flags & PROCESS_FLAG_LINUX_CTXT; - PCPMetric_enable(PCP_PROC_VCTXSW, flagged && enabled); - PCPMetric_enable(PCP_PROC_NVCTXSW, flagged && enabled); - flagged = settings->ss->flags & PROCESS_FLAG_LINUX_SECATTR; - PCPMetric_enable(PCP_PROC_LABELS, flagged && enabled); - flagged = settings->ss->flags & PROCESS_FLAG_LINUX_AUTOGROUP; - PCPMetric_enable(PCP_PROC_AUTOGROUP_ID, flagged && enabled); - PCPMetric_enable(PCP_PROC_AUTOGROUP_NICE, flagged && enabled); - - /* Sample smaps metrics on every second pass to improve performance */ - static int smaps_flag; - smaps_flag = !!smaps_flag; - PCPMetric_enable(PCP_PROC_SMAPS_PSS, smaps_flag && enabled); - PCPMetric_enable(PCP_PROC_SMAPS_SWAP, smaps_flag && enabled); - PCPMetric_enable(PCP_PROC_SMAPS_SWAPPSS, smaps_flag && enabled); - - struct timeval timestamp; - if (PCPMetric_fetch(×tamp) != true) - return; - - double sample = this->timestamp; - this->timestamp = pmtimevalToReal(×tamp); - - PCPProcessList_updateHeader(super, settings); - - /* In pause mode only update global data for meters (CPU, memory, etc) */ - if (pauseProcessUpdate) - return; - - double period = (this->timestamp - sample) * 100; - PCPProcessList_updateProcesses(this, period, ×tamp); -} - -Machine* Machine_new(UsersTable* usersTable, uid_t userId) { - Machine* this = xCalloc(1, sizeof(Machine)); - Machine_init(this, usersTable, userId); - return this; -} - -void Machine_delete(Machine* host) { - free(host); -} - -bool Machine_isCPUonline(const Machine* host, unsigned int id) { - assert(id < host->existingCPUs); - (void) host; - - pmAtomValue value; - if (PCPMetric_instance(PCP_PERCPU_SYSTEM, id, id, &value, PM_TYPE_U32)) - return true; - return false; + PCPProcessList_updateProcesses(this); } diff --git a/pcp/PCPProcessList.h b/pcp/PCPProcessList.h index 9bce9cd73..47a360263 100644 --- a/pcp/PCPProcessList.h +++ b/pcp/PCPProcessList.h @@ -17,62 +17,10 @@ in the source distribution for its full text. #include "UsersTable.h" #include "pcp/Platform.h" -#include "zfs/ZfsArcStats.h" -typedef enum CPUMetric_ { - CPU_TOTAL_TIME, - CPU_USER_TIME, - CPU_SYSTEM_TIME, - CPU_SYSTEM_ALL_TIME, - CPU_IDLE_ALL_TIME, - CPU_IDLE_TIME, - CPU_NICE_TIME, - CPU_IOWAIT_TIME, - CPU_IRQ_TIME, - CPU_SOFTIRQ_TIME, - CPU_STEAL_TIME, - CPU_GUEST_TIME, - CPU_GUESTNICE_TIME, - - CPU_TOTAL_PERIOD, - CPU_USER_PERIOD, - CPU_SYSTEM_PERIOD, - CPU_SYSTEM_ALL_PERIOD, - CPU_IDLE_ALL_PERIOD, - CPU_IDLE_PERIOD, - CPU_NICE_PERIOD, - CPU_IOWAIT_PERIOD, - CPU_IRQ_PERIOD, - CPU_SOFTIRQ_PERIOD, - CPU_STEAL_PERIOD, - CPU_GUEST_PERIOD, - CPU_GUESTNICE_PERIOD, - - CPU_FREQUENCY, - - CPU_METRIC_COUNT -} CPUMetric; - typedef struct PCPProcessList_ { ProcessList super; - double timestamp; /* previous sample timestamp */ - pmAtomValue* cpu; /* aggregate values for each metric */ - pmAtomValue** percpu; /* per-processor values for each metric */ - pmAtomValue* values; /* per-processor buffer for just one metric */ - ZfsArcStats zfs; } PCPProcessList; -ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList); - -void ProcessList_delete(ProcessList* pl); - -void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate); - -Machine* Machine_new(UsersTable* usersTable, uid_t userId); - -bool Machine_isCPUonline(const Machine* host, unsigned int id); - -void Machine_delete(Machine* host); - #endif diff --git a/pcp/Platform.c b/pcp/Platform.c index 181898aaa..12e0f4d78 100644 --- a/pcp/Platform.c +++ b/pcp/Platform.c @@ -46,6 +46,7 @@ in the source distribution for its full text. #include "linux/ZramStats.h" #include "pcp/PCPDynamicColumn.h" #include "pcp/PCPDynamicMeter.h" +#include "pcp/PCPMachine.h" #include "pcp/PCPMetric.h" #include "pcp/PCPProcessList.h" #include "zfs/ZfsArcMeter.h" @@ -484,9 +485,7 @@ long long Platform_getBootTime(void) { return pcp->btime; } -static double Platform_setOneCPUValues(Meter* this, pmAtomValue* values) { - Settings* settings = this->host->settings; - +static double Platform_setOneCPUValues(Meter* this, const Settings* settings, pmAtomValue* values) { unsigned long long value = values[CPU_TOTAL_PERIOD].ull; double total = (double) (value == 0 ? 1 : value); double percent; @@ -524,18 +523,19 @@ static double Platform_setOneCPUValues(Meter* this, pmAtomValue* values) { } double Platform_setCPUValues(Meter* this, int cpu) { - const PCPProcessList* pl = (const PCPProcessList*) this->host->pl; + const PCPMachine* phost = (const PCPMachine*) this->host; + const Settings* settings = this->host->settings; + if (cpu <= 0) /* use aggregate values */ - return Platform_setOneCPUValues(this, pl->cpu); - return Platform_setOneCPUValues(this, pl->percpu[cpu - 1]); + return Platform_setOneCPUValues(this, settings, phost->cpu); + return Platform_setOneCPUValues(this, settings, phost->percpu[cpu - 1]); } void Platform_setMemoryValues(Meter* this) { const Machine* host = this->host; - const ProcessList* pl = host->pl; - const PCPProcessList* ppl = (const PCPProcessList*) pl; + const PCPMachine* phost = (const PCPMachine*) host; - this->total = host->totalMem; + this->total = host->totalMem; this->values[MEMORY_METER_USED] = host->usedMem; this->values[MEMORY_METER_BUFFERS] = host->buffersMem; this->values[MEMORY_METER_SHARED] = host->sharedMem; @@ -543,11 +543,11 @@ void Platform_setMemoryValues(Meter* this) { this->values[MEMORY_METER_CACHE] = host->cachedMem; this->values[MEMORY_METER_AVAILABLE] = host->availableMem; - if (ppl->zfs.enabled != 0) { + if (phost->zfs.enabled != 0) { // ZFS does not shrink below the value of zfs_arc_min. unsigned long long int shrinkableSize = 0; - if (ppl->zfs.size > ppl->zfs.min) - shrinkableSize = ppl->zfs.size - ppl->zfs.min; + if (phost->zfs.size > phost->zfs.min) + shrinkableSize = phost->zfs.size - phost->zfs.min; this->values[MEMORY_METER_USED] -= shrinkableSize; this->values[MEMORY_METER_CACHE] += shrinkableSize; this->values[MEMORY_METER_AVAILABLE] += shrinkableSize; @@ -595,15 +595,15 @@ void Platform_setZramValues(Meter* this) { } void Platform_setZfsArcValues(Meter* this) { - const PCPProcessList* ppl = (const PCPProcessList*) this->host->pl; + const PCPMachine* phost = (const PCPMachine*) this->host; - ZfsArcMeter_readStats(this, &(ppl->zfs)); + ZfsArcMeter_readStats(this, &phost->zfs); } void Platform_setZfsCompressedArcValues(Meter* this) { - const PCPProcessList* ppl = (const PCPProcessList*) this->host->pl; + const PCPMachine* phost = (const PCPMachine*) this->host; - ZfsCompressedArcMeter_readStats(this, &(ppl->zfs)); + ZfsCompressedArcMeter_readStats(this, &phost->zfs); } void Platform_getHostname(char* buffer, size_t size) { diff --git a/solaris/Platform.c b/solaris/Platform.c index 8c88fc850..ad899492d 100644 --- a/solaris/Platform.c +++ b/solaris/Platform.c @@ -34,10 +34,11 @@ in the source distribution for its full text. #include "HostnameMeter.h" #include "SysArchMeter.h" #include "UptimeMeter.h" + +#include "solaris/SolarisMachine.h" + #include "zfs/ZfsArcMeter.h" #include "zfs/ZfsCompressedArcMeter.h" -#include "SolarisProcess.h" -#include "SolarisProcessList.h" const ScreenDefaults Platform_defaultScreens[] = { @@ -195,15 +196,15 @@ int Platform_getMaxPid(void) { double Platform_setCPUValues(Meter* this, unsigned int cpu) { const Machine* host = this->host; - const SolarisProcessList* spl = (const SolarisProcessList*) host->pl; + const SolarisMachine* shost = (const SolarisMachine*) host; unsigned int cpus = host->existingCPUs; const CPUData* cpuData = NULL; if (cpus == 1) { // single CPU box has everything in spl->cpus[0] - cpuData = &(spl->cpus[0]); + cpuData = &(shost->cpus[0]); } else { - cpuData = &(spl->cpus[cpu]); + cpuData = &(shost->cpus[cpu]); } if (!cpuData->online) { @@ -216,7 +217,7 @@ double Platform_setCPUValues(Meter* this, unsigned int cpu) { v[CPU_METER_NICE] = cpuData->nicePercent; v[CPU_METER_NORMAL] = cpuData->userPercent; - if (host->settings->detailedCPUTime) { + if (super->settings->detailedCPUTime) { v[CPU_METER_KERNEL] = cpuData->systemPercent; v[CPU_METER_IRQ] = cpuData->irqPercent; this->curItems = 4; @@ -255,15 +256,15 @@ void Platform_setSwapValues(Meter* this) { } void Platform_setZfsArcValues(Meter* this) { - const SolarisProcessList* spl = (const SolarisProcessList*) this->host->pl; + const SolarisMachine* shost = (SolarisMachine*) this->host; - ZfsArcMeter_readStats(this, &(spl->zfs)); + ZfsArcMeter_readStats(this, &(shost->zfs)); } void Platform_setZfsCompressedArcValues(Meter* this) { - const SolarisProcessList* spl = (const SolarisProcessList*) this->host->pl; + const SolarisMachine* shost = (SolarisMachine*) this->host; - ZfsCompressedArcMeter_readStats(this, &(spl->zfs)); + ZfsCompressedArcMeter_readStats(this, &(shost->zfs)); } static int Platform_buildenv(void* accum, struct ps_prochandle* Phandle, uintptr_t addr, const char* str) { diff --git a/solaris/SolarisMachine.c b/solaris/SolarisMachine.c new file mode 100644 index 000000000..9ce4ced21 --- /dev/null +++ b/solaris/SolarisMachine.c @@ -0,0 +1,332 @@ +/* +htop - SolarisMachine.c +(C) 2014 Hisham H. Muhammad +(C) 2017,2018 Guy M. Broome +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + + +#include "solaris/SolarisMachine.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "CRT.h" +#include "solaris/Platform.h" + + +static void SolarisMachine_updateCPUcount(SolarisMachine* this) { + Machine* super = &this->super; + long int s; + bool change = false; + + s = sysconf(_SC_NPROCESSORS_CONF); + if (s < 1) + CRT_fatalError("Cannot get existing CPU count by sysconf(_SC_NPROCESSORS_CONF)"); + + if (s != super->existingCPUs) { + if (s == 1) { + this->cpus = xRealloc(this->cpus, sizeof(CPUData)); + this->cpus[0].online = true; + } else { + this->cpus = xReallocArray(this->cpus, s + 1, sizeof(CPUData)); + this->cpus[0].online = true; /* average is always "online" */ + for (int i = 1; i < s + 1; i++) { + this->cpus[i].online = false; + } + } + + change = true; + super->existingCPUs = s; + } + + s = sysconf(_SC_NPROCESSORS_ONLN); + if (s < 1) + CRT_fatalError("Cannot get active CPU count by sysconf(_SC_NPROCESSORS_ONLN)"); + + if (s != super->activeCPUs) { + change = true; + hsuper->activeCPUs = s; + } + + if (change) { + kstat_close(this->kd); + this->kd = kstat_open(); + if (!this->kd) + CRT_fatalError("Cannot open kstat handle"); + } +} + + +static void SolarisMachine_scanCPUTime(SolarisMachine* this) { + Machine* super = &this->super; + unsigned int activeCPUs = super->activeCPUs; + unsigned int existingCPUs = super->existingCPUs; + kstat_t* cpuinfo = NULL; + kstat_named_t* idletime = NULL; + kstat_named_t* intrtime = NULL; + kstat_named_t* krnltime = NULL; + kstat_named_t* usertime = NULL; + kstat_named_t* cpu_freq = NULL; + double idlebuf = 0; + double intrbuf = 0; + double krnlbuf = 0; + double userbuf = 0; + int arrskip = 0; + + assert(existingCPUs > 0); + assert(this->kd); + + if (existingCPUs > 1) { + // Store values for the stats loop one extra element up in the array + // to leave room for the average to be calculated afterwards + arrskip++; + } + + // Calculate per-CPU statistics first + for (unsigned int i = 0; i < existingCPUs; i++) { + CPUData* cpuData = &(this->cpus[i + arrskip]); + + if ((cpuinfo = kstat_lookup_wrapper(this->kd, "cpu", i, "sys")) != NULL) { + cpuData->online = true; + if (kstat_read(this->kd, cpuinfo, NULL) != -1) { + idletime = kstat_data_lookup_wrapper(cpuinfo, "cpu_nsec_idle"); + intrtime = kstat_data_lookup_wrapper(cpuinfo, "cpu_nsec_intr"); + krnltime = kstat_data_lookup_wrapper(cpuinfo, "cpu_nsec_kernel"); + usertime = kstat_data_lookup_wrapper(cpuinfo, "cpu_nsec_user"); + } + } else { + cpuData->online = false; + continue; + } + + assert( (idletime != NULL) && (intrtime != NULL) + && (krnltime != NULL) && (usertime != NULL) ); + + if (super->settings->showCPUFrequency) { + if ((cpuinfo = kstat_lookup_wrapper(this->kd, "cpu_info", i, NULL)) != NULL) { + if (kstat_read(this->kd, cpuinfo, NULL) != -1) { + cpu_freq = kstat_data_lookup_wrapper(cpuinfo, "current_clock_Hz"); + } + } + + assert( cpu_freq != NULL ); + } + + uint64_t totaltime = (idletime->value.ui64 - cpuData->lidle) + + (intrtime->value.ui64 - cpuData->lintr) + + (krnltime->value.ui64 - cpuData->lkrnl) + + (usertime->value.ui64 - cpuData->luser); + + // Calculate percentages of deltas since last reading + cpuData->userPercent = ((usertime->value.ui64 - cpuData->luser) / (double)totaltime) * 100.0; + cpuData->nicePercent = (double)0.0; // Not implemented on Solaris + cpuData->systemPercent = ((krnltime->value.ui64 - cpuData->lkrnl) / (double)totaltime) * 100.0; + cpuData->irqPercent = ((intrtime->value.ui64 - cpuData->lintr) / (double)totaltime) * 100.0; + cpuData->systemAllPercent = cpuData->systemPercent + cpuData->irqPercent; + cpuData->idlePercent = ((idletime->value.ui64 - cpuData->lidle) / (double)totaltime) * 100.0; + // Store current values to use for the next round of deltas + cpuData->luser = usertime->value.ui64; + cpuData->lkrnl = krnltime->value.ui64; + cpuData->lintr = intrtime->value.ui64; + cpuData->lidle = idletime->value.ui64; + // Add frequency in MHz + cpuData->frequency = super->settings->showCPUFrequency ? (double)cpu_freq->value.ui64 / 1E6 : NAN; + // Accumulate the current percentages into buffers for later average calculation + if (existingCPUs > 1) { + userbuf += cpuData->userPercent; + krnlbuf += cpuData->systemPercent; + intrbuf += cpuData->irqPercent; + idlebuf += cpuData->idlePercent; + } + } + + if (existingCPUs > 1) { + CPUData* cpuData = &(this->cpus[0]); + cpuData->userPercent = userbuf / activeCPUs; + cpuData->nicePercent = (double)0.0; // Not implemented on Solaris + cpuData->systemPercent = krnlbuf / activeCPUs; + cpuData->irqPercent = intrbuf / activeCPUs; + cpuData->systemAllPercent = cpuData->systemPercent + cpuData->irqPercent; + cpuData->idlePercent = idlebuf / activeCPUs; + } +} + +static void SolarisMachine_scanMemoryInfo(SolarisMachine* this) { + Machine* super = &this->super; + static kstat_t *meminfo = NULL; + int ksrphyserr = -1; + kstat_named_t *totalmem_pgs = NULL; + kstat_named_t *freemem_pgs = NULL; + kstat_named_t *pages = NULL; + struct swaptable *sl = NULL; + struct swapent *swapdev = NULL; + uint64_t totalswap = 0; + uint64_t totalfree = 0; + int nswap = 0; + char *spath = NULL; + char *spathbase = NULL; + + // Part 1 - physical memory + if (this->kd != NULL && meminfo == NULL) { + // Look up the kstat chain just once, it never changes + meminfo = kstat_lookup_wrapper(this->kd, "unix", 0, "system_pages"); + } + if (meminfo != NULL) { + ksrphyserr = kstat_read(this->kd, meminfo, NULL); + } + if (ksrphyserr != -1) { + totalmem_pgs = kstat_data_lookup_wrapper(meminfo, "physmem"); + freemem_pgs = kstat_data_lookup_wrapper(meminfo, "freemem"); + pages = kstat_data_lookup_wrapper(meminfo, "pagestotal"); + + super->totalMem = totalmem_pgs->value.ui64 * this->pageSizeKB; + if (super->totalMem > freemem_pgs->value.ui64 * this->pageSizeKB) { + super->usedMem = super->totalMem - freemem_pgs->value.ui64 * this->pageSizeKB; + } else { + super->usedMem = 0; // This can happen in non-global zone (in theory) + } + // Not sure how to implement this on Solaris - suggestions welcome! + super->cachedMem = 0; + // Not really "buffers" but the best Solaris analogue that I can find to + // "memory in use but not by programs or the kernel itself" + super->buffersMem = (totalmem_pgs->value.ui64 - pages->value.ui64) * this->pageSizeKB; + } else { + // Fall back to basic sysconf if kstat isn't working + super->totalMem = sysconf(_SC_PHYS_PAGES) * this->pageSize; + super->buffersMem = 0; + super->cachedMem = 0; + super->usedMem = super->totalMem - (sysconf(_SC_AVPHYS_PAGES) * this->pageSize); + } + + // Part 2 - swap + nswap = swapctl(SC_GETNSWP, NULL); + if (nswap > 0) { + sl = xMalloc((nswap * sizeof(swapent_t)) + sizeof(int)); + } + if (sl != NULL) { + spathbase = xMalloc( nswap * MAXPATHLEN ); + } + if (spathbase != NULL) { + spath = spathbase; + swapdev = sl->swt_ent; + for (int i = 0; i < nswap; i++, swapdev++) { + swapdev->ste_path = spath; + spath += MAXPATHLEN; + } + sl->swt_n = nswap; + } + nswap = swapctl(SC_LIST, sl); + if (nswap > 0) { + swapdev = sl->swt_ent; + for (int i = 0; i < nswap; i++, swapdev++) { + totalswap += swapdev->ste_pages; + totalfree += swapdev->ste_free; + } + } + free(spathbase); + free(sl); + super->totalSwap = totalswap * this->pageSizeKB; + super->usedSwap = super->totalSwap - (totalfree * this->pageSizeKB); +} + +static void SolarisMachine_scanZfsArcstats(SolarisMachine* this) { + kstat_named_t *cur_kstat = NULL; + kstat_t *arcstats = NULL; + int ksrphyserr = -1; + + if (this->kd != NULL) { + arcstats = kstat_lookup_wrapper(this->kd, "zfs", 0, "arcstats"); + } + if (arcstats != NULL) { + ksrphyserr = kstat_read(this->kd, arcstats, NULL); + } + if (ksrphyserr != -1) { + cur_kstat = kstat_data_lookup_wrapper( arcstats, "size" ); + this->zfs.size = cur_kstat->value.ui64 / 1024; + this->zfs.enabled = this->zfs.size > 0 ? 1 : 0; + + cur_kstat = kstat_data_lookup_wrapper( arcstats, "c_max" ); + this->zfs.max = cur_kstat->value.ui64 / 1024; + + cur_kstat = kstat_data_lookup_wrapper( arcstats, "mfu_size" ); + this->zfs.MFU = cur_kstat != NULL ? cur_kstat->value.ui64 / 1024 : 0; + + cur_kstat = kstat_data_lookup_wrapper( arcstats, "mru_size" ); + this->zfs.MRU = cur_kstat != NULL ? cur_kstat->value.ui64 / 1024 : 0; + + cur_kstat = kstat_data_lookup_wrapper( arcstats, "anon_size" ); + this->zfs.anon = cur_kstat != NULL ? cur_kstat->value.ui64 / 1024 : 0; + + cur_kstat = kstat_data_lookup_wrapper( arcstats, "hdr_size" ); + this->zfs.header = cur_kstat != NULL ? cur_kstat->value.ui64 / 1024 : 0; + + cur_kstat = kstat_data_lookup_wrapper( arcstats, "other_size" ); + this->zfs.other = cur_kstat != NULL ? cur_kstat->value.ui64 / 1024 : 0; + + if ((cur_kstat = kstat_data_lookup_wrapper( arcstats, "compressed_size" )) != NULL) { + this->zfs.compressed = cur_kstat->value.ui64 / 1024; + this->zfs.isCompressed = 1; + + cur_kstat = kstat_data_lookup_wrapper( arcstats, "uncompressed_size" ); + this->zfs.uncompressed = cur_kstat->value.ui64 / 1024; + } else { + this->zfs.isCompressed = 0; + } + } +} + +void Machine_scan(Machine* super) { + SolarisMachine* this = (SolarisMachine*) super; + + SolarisMachine_updateCPUcount(this); + SolarisMachine_scanCPUTime(this); + SolarisMachine_scanMemoryInfo(this); + SolarisMachine_scanZfsArcstats(this); +} + +Machine* Machine_new(UsersTable* usersTable, uid_t userId) { + SolarisMachine* this = xCalloc(1, sizeof(SolarisMachine)); + Machine *super = &this->super; + + Machine_init(super, usersTable, userId); + + this->pageSize = sysconf(_SC_PAGESIZE); + if (this->pageSize == -1) + CRT_fatalError("Cannot get pagesize by sysconf(_SC_PAGESIZE)"); + this->pageSizeKB = this->pageSize / 1024; + + SolarisMachine_updateCPUcount(this); + + return super; +} + +void Machine_delete(Machine* super) { + SolarisMachine* this = (SolarisMachine*) super; + + Machine_done(super); + + free(this->cpus); + if (this->kd) { + kstat_close(this->kd); + } + free(this); +} + +bool Machine_isCPUonline(const Machine* super, unsigned int id) { + assert(id < super->existingCPUs); + + const SolarisMachine* this = (const SolarisMachine*) super; + + return (super->existingCPUs == 1) ? true : this->cpus[id + 1].online; +} diff --git a/solaris/SolarisMachine.h b/solaris/SolarisMachine.h new file mode 100644 index 000000000..da091c6e9 --- /dev/null +++ b/solaris/SolarisMachine.h @@ -0,0 +1,59 @@ +#ifndef HEADER_SolarisMachine +#define HEADER_SolarisMachine +/* +htop - SolarisMachine.h +(C) 2014 Hisham H. Muhammad +(C) 2017,2018 Guy M. Broome +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "config.h" // IWYU pragma: keep + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Hashtable.h" +#include "UsersTable.h" + +#include "zfs/ZfsArcStats.h" + + +#define ZONE_ERRMSGLEN 1024 +extern char zone_errmsg[ZONE_ERRMSGLEN]; + +typedef struct CPUData_ { + double userPercent; + double nicePercent; + double systemPercent; + double irqPercent; + double idlePercent; + double systemAllPercent; + double frequency; + uint64_t luser; + uint64_t lkrnl; + uint64_t lintr; + uint64_t lidle; + bool online; +} CPUData; + +typedef struct SolarisMachine_ { + Machine super; + + kstat_ctl_t* kd; + CPUData* cpus; + + int pageSize; + int pageSizeKB; + + ZfsArcStats zfs; +} SolarisMachine; + +#endif diff --git a/solaris/SolarisProcessList.c b/solaris/SolarisProcessList.c index 84991d6e1..e759b1877 100644 --- a/solaris/SolarisProcessList.c +++ b/solaris/SolarisProcessList.c @@ -29,9 +29,6 @@ in the source distribution for its full text. #define GZONE "global " #define UZONE "unknown " -static int pageSize; -static int pageSizeKB; - static char* SolarisProcessList_readZoneName(kstat_ctl_t* kd, SolarisProcess* sproc) { char* zname; @@ -47,296 +44,18 @@ static char* SolarisProcessList_readZoneName(kstat_ctl_t* kd, SolarisProcess* sp return zname; } -static void SolarisProcessList_updateCPUcount(ProcessList* super) { - Machine* host = super->host; - SolarisProcessList* spl = (SolarisProcessList*) super; - long int s; - bool change = false; - - s = sysconf(_SC_NPROCESSORS_CONF); - if (s < 1) - CRT_fatalError("Cannot get existing CPU count by sysconf(_SC_NPROCESSORS_CONF)"); - - if (s != host->existingCPUs) { - if (s == 1) { - spl->cpus = xRealloc(spl->cpus, sizeof(CPUData)); - spl->cpus[0].online = true; - } else { - spl->cpus = xReallocArray(spl->cpus, s + 1, sizeof(CPUData)); - spl->cpus[0].online = true; /* average is always "online" */ - for (int i = 1; i < s + 1; i++) { - spl->cpus[i].online = false; - } - } - - change = true; - host->existingCPUs = s; - } - - s = sysconf(_SC_NPROCESSORS_ONLN); - if (s < 1) - CRT_fatalError("Cannot get active CPU count by sysconf(_SC_NPROCESSORS_ONLN)"); - - if (s != host->activeCPUs) { - change = true; - host->activeCPUs = s; - } - - if (change) { - kstat_close(spl->kd); - spl->kd = kstat_open(); - if (!spl->kd) - CRT_fatalError("Cannot open kstat handle"); - } -} - ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList) { SolarisProcessList* spl = xCalloc(1, sizeof(SolarisProcessList)); ProcessList* pl = (ProcessList*) spl; - ProcessList_init(pl, Class(SolarisProcess), host, pidMatchList); - - spl->kd = kstat_open(); - if (!spl->kd) - CRT_fatalError("Cannot open kstat handle"); - pageSize = sysconf(_SC_PAGESIZE); - if (pageSize == -1) - CRT_fatalError("Cannot get pagesize by sysconf(_SC_PAGESIZE)"); - pageSizeKB = pageSize / 1024; - - SolarisProcessList_updateCPUcount(pl); + ProcessList_init(pl, Class(SolarisProcess), host, pidMatchList); return pl; } -static inline void SolarisProcessList_scanCPUTime(ProcessList* pl) { - const SolarisProcessList* spl = (SolarisProcessList*) pl; - unsigned int activeCPUs = pl->host->activeCPUs; - unsigned int existingCPUs = pl->host->existingCPUs; - kstat_t* cpuinfo = NULL; - kstat_named_t* idletime = NULL; - kstat_named_t* intrtime = NULL; - kstat_named_t* krnltime = NULL; - kstat_named_t* usertime = NULL; - kstat_named_t* cpu_freq = NULL; - double idlebuf = 0; - double intrbuf = 0; - double krnlbuf = 0; - double userbuf = 0; - int arrskip = 0; - - assert(existingCPUs > 0); - assert(spl->kd); - - if (existingCPUs > 1) { - // Store values for the stats loop one extra element up in the array - // to leave room for the average to be calculated afterwards - arrskip++; - } - - // Calculate per-CPU statistics first - for (unsigned int i = 0; i < existingCPUs; i++) { - CPUData* cpuData = &(spl->cpus[i + arrskip]); - - if ((cpuinfo = kstat_lookup_wrapper(spl->kd, "cpu", i, "sys")) != NULL) { - cpuData->online = true; - if (kstat_read(spl->kd, cpuinfo, NULL) != -1) { - idletime = kstat_data_lookup_wrapper(cpuinfo, "cpu_nsec_idle"); - intrtime = kstat_data_lookup_wrapper(cpuinfo, "cpu_nsec_intr"); - krnltime = kstat_data_lookup_wrapper(cpuinfo, "cpu_nsec_kernel"); - usertime = kstat_data_lookup_wrapper(cpuinfo, "cpu_nsec_user"); - } - } else { - cpuData->online = false; - continue; - } - - assert( (idletime != NULL) && (intrtime != NULL) - && (krnltime != NULL) && (usertime != NULL) ); - - if (pl->settings->showCPUFrequency) { - if ((cpuinfo = kstat_lookup_wrapper(spl->kd, "cpu_info", i, NULL)) != NULL) { - if (kstat_read(spl->kd, cpuinfo, NULL) != -1) { - cpu_freq = kstat_data_lookup_wrapper(cpuinfo, "current_clock_Hz"); - } - } - - assert( cpu_freq != NULL ); - } - - uint64_t totaltime = (idletime->value.ui64 - cpuData->lidle) - + (intrtime->value.ui64 - cpuData->lintr) - + (krnltime->value.ui64 - cpuData->lkrnl) - + (usertime->value.ui64 - cpuData->luser); - - // Calculate percentages of deltas since last reading - cpuData->userPercent = ((usertime->value.ui64 - cpuData->luser) / (double)totaltime) * 100.0; - cpuData->nicePercent = (double)0.0; // Not implemented on Solaris - cpuData->systemPercent = ((krnltime->value.ui64 - cpuData->lkrnl) / (double)totaltime) * 100.0; - cpuData->irqPercent = ((intrtime->value.ui64 - cpuData->lintr) / (double)totaltime) * 100.0; - cpuData->systemAllPercent = cpuData->systemPercent + cpuData->irqPercent; - cpuData->idlePercent = ((idletime->value.ui64 - cpuData->lidle) / (double)totaltime) * 100.0; - // Store current values to use for the next round of deltas - cpuData->luser = usertime->value.ui64; - cpuData->lkrnl = krnltime->value.ui64; - cpuData->lintr = intrtime->value.ui64; - cpuData->lidle = idletime->value.ui64; - // Add frequency in MHz - cpuData->frequency = pl->settings->showCPUFrequency ? (double)cpu_freq->value.ui64 / 1E6 : NAN; - // Accumulate the current percentages into buffers for later average calculation - if (existingCPUs > 1) { - userbuf += cpuData->userPercent; - krnlbuf += cpuData->systemPercent; - intrbuf += cpuData->irqPercent; - idlebuf += cpuData->idlePercent; - } - } - - if (existingCPUs > 1) { - CPUData* cpuData = &(spl->cpus[0]); - cpuData->userPercent = userbuf / activeCPUs; - cpuData->nicePercent = (double)0.0; // Not implemented on Solaris - cpuData->systemPercent = krnlbuf / activeCPUs; - cpuData->irqPercent = intrbuf / activeCPUs; - cpuData->systemAllPercent = cpuData->systemPercent + cpuData->irqPercent; - cpuData->idlePercent = idlebuf / activeCPUs; - } -} - -static inline void SolarisProcessList_scanMemoryInfo(ProcessList* pl) { - Machine* host = pl->host; - SolarisProcessList* spl = (SolarisProcessList*) pl; - - static kstat_t *meminfo = NULL; - int ksrphyserr = -1; - kstat_named_t *totalmem_pgs = NULL; - kstat_named_t *freemem_pgs = NULL; - kstat_named_t *pages = NULL; - struct swaptable *sl = NULL; - struct swapent *swapdev = NULL; - uint64_t totalswap = 0; - uint64_t totalfree = 0; - int nswap = 0; - char *spath = NULL; - char *spathbase = NULL; - - // Part 1 - physical memory - if (spl->kd != NULL && meminfo == NULL) { - // Look up the kstat chain just once, it never changes - meminfo = kstat_lookup_wrapper(spl->kd, "unix", 0, "system_pages"); - } - if (meminfo != NULL) { - ksrphyserr = kstat_read(spl->kd, meminfo, NULL); - } - if (ksrphyserr != -1) { - totalmem_pgs = kstat_data_lookup_wrapper(meminfo, "physmem"); - freemem_pgs = kstat_data_lookup_wrapper(meminfo, "freemem"); - pages = kstat_data_lookup_wrapper(meminfo, "pagestotal"); - - host->totalMem = totalmem_pgs->value.ui64 * pageSizeKB; - if (host->totalMem > freemem_pgs->value.ui64 * pageSizeKB) { - host->usedMem = host->totalMem - freemem_pgs->value.ui64 * pageSizeKB; - } else { - host->usedMem = 0; // This can happen in non-global zone (in theory) - } - // Not sure how to implement this on Solaris - suggestions welcome! - host->cachedMem = 0; - // Not really "buffers" but the best Solaris analogue that I can find to - // "memory in use but not by programs or the kernel itself" - host->buffersMem = (totalmem_pgs->value.ui64 - pages->value.ui64) * pageSizeKB; - } else { - // Fall back to basic sysconf if kstat isn't working - host->totalMem = sysconf(_SC_PHYS_PAGES) * pageSize; - host->buffersMem = 0; - host->cachedMem = 0; - host->usedMem = host->totalMem - (sysconf(_SC_AVPHYS_PAGES) * pageSize); - } - - // Part 2 - swap - nswap = swapctl(SC_GETNSWP, NULL); - if (nswap > 0) { - sl = xMalloc((nswap * sizeof(swapent_t)) + sizeof(int)); - } - if (sl != NULL) { - spathbase = xMalloc( nswap * MAXPATHLEN ); - } - if (spathbase != NULL) { - spath = spathbase; - swapdev = sl->swt_ent; - for (int i = 0; i < nswap; i++, swapdev++) { - swapdev->ste_path = spath; - spath += MAXPATHLEN; - } - sl->swt_n = nswap; - } - nswap = swapctl(SC_LIST, sl); - if (nswap > 0) { - swapdev = sl->swt_ent; - for (int i = 0; i < nswap; i++, swapdev++) { - totalswap += swapdev->ste_pages; - totalfree += swapdev->ste_free; - } - } - free(spathbase); - free(sl); - host->totalSwap = totalswap * pageSizeKB; - host->usedSwap = host->totalSwap - (totalfree * pageSizeKB); -} - -static inline void SolarisProcessList_scanZfsArcstats(ProcessList* pl) { - SolarisProcessList* spl = (SolarisProcessList*) pl; - kstat_t *arcstats = NULL; - int ksrphyserr = -1; - kstat_named_t *cur_kstat = NULL; - - if (spl->kd != NULL) { - arcstats = kstat_lookup_wrapper(spl->kd, "zfs", 0, "arcstats"); - } - if (arcstats != NULL) { - ksrphyserr = kstat_read(spl->kd, arcstats, NULL); - } - if (ksrphyserr != -1) { - cur_kstat = kstat_data_lookup_wrapper( arcstats, "size" ); - spl->zfs.size = cur_kstat->value.ui64 / 1024; - spl->zfs.enabled = spl->zfs.size > 0 ? 1 : 0; - - cur_kstat = kstat_data_lookup_wrapper( arcstats, "c_max" ); - spl->zfs.max = cur_kstat->value.ui64 / 1024; - - cur_kstat = kstat_data_lookup_wrapper( arcstats, "mfu_size" ); - spl->zfs.MFU = cur_kstat != NULL ? cur_kstat->value.ui64 / 1024 : 0; - - cur_kstat = kstat_data_lookup_wrapper( arcstats, "mru_size" ); - spl->zfs.MRU = cur_kstat != NULL ? cur_kstat->value.ui64 / 1024 : 0; - - cur_kstat = kstat_data_lookup_wrapper( arcstats, "anon_size" ); - spl->zfs.anon = cur_kstat != NULL ? cur_kstat->value.ui64 / 1024 : 0; - - cur_kstat = kstat_data_lookup_wrapper( arcstats, "hdr_size" ); - spl->zfs.header = cur_kstat != NULL ? cur_kstat->value.ui64 / 1024 : 0; - - cur_kstat = kstat_data_lookup_wrapper( arcstats, "other_size" ); - spl->zfs.other = cur_kstat != NULL ? cur_kstat->value.ui64 / 1024 : 0; - - if ((cur_kstat = kstat_data_lookup_wrapper( arcstats, "compressed_size" )) != NULL) { - spl->zfs.compressed = cur_kstat->value.ui64 / 1024; - spl->zfs.isCompressed = 1; - - cur_kstat = kstat_data_lookup_wrapper( arcstats, "uncompressed_size" ); - spl->zfs.uncompressed = cur_kstat->value.ui64 / 1024; - } else { - spl->zfs.isCompressed = 0; - } - } -} - void ProcessList_delete(ProcessList* pl) { SolarisProcessList* spl = (SolarisProcessList*) pl; ProcessList_done(pl); - free(spl->cpus); - if (spl->kd) { - kstat_close(spl->kd); - } free(spl); } @@ -392,7 +111,7 @@ static int SolarisProcessList_walkproc(psinfo_t* _psinfo, lwpsinfo_t* _lwpsinfo, // Setup process list ProcessList* pl = (ProcessList*) listptr; SolarisProcessList* spl = (SolarisProcessList*) listptr; - Machine* host = pl->super.host; + Machine* host = pl->host; id_t lwpid_real = _lwpsinfo->pr_lwpid; if (lwpid_real > 1023) { @@ -407,8 +126,9 @@ static int SolarisProcessList_walkproc(psinfo_t* _psinfo, lwpsinfo_t* _lwpsinfo, getpid = lwpid; } - Process* proc = ProcessList_getProcess(pl, getpid, &preExisting, SolarisProcess_new); - SolarisProcess* sproc = (SolarisProcess*) proc; + Process* proc = ProcessList_getProcess(pl, getpid, &preExisting, SolarisProcess_new); + SolarisProcess* sproc = (SolarisProcess*) proc; + const Settings* settings = host->settings; // Common code pass 1 proc->show = false; @@ -455,7 +175,7 @@ static int SolarisProcessList_walkproc(psinfo_t* _psinfo, lwpsinfo_t* _lwpsinfo, Process_updateComm(proc, _psinfo->pr_fname); Process_updateCmdline(proc, _psinfo->pr_psargs, 0, 0); - if (proc->settings->ss->flags & PROCESS_FLAG_CWD) { + if (settings->ss->flags & PROCESS_FLAG_CWD) { SolarisProcessList_updateCwd(_psinfo->pr_pid, proc); } } @@ -479,7 +199,7 @@ static int SolarisProcessList_walkproc(psinfo_t* _psinfo, lwpsinfo_t* _lwpsinfo, } // Update proc and thread counts based on settings - if (proc->isKernelThread && !pl->settings->hideKernelThreads) { + if (proc->isKernelThread && !settings->hideKernelThreads) { pl->kernelThreads += proc->nlwp; pl->totalTasks += proc->nlwp + 1; if (proc->state == RUNNING) { @@ -489,14 +209,14 @@ static int SolarisProcessList_walkproc(psinfo_t* _psinfo, lwpsinfo_t* _lwpsinfo, if (proc->state == RUNNING) { pl->runningTasks++; } - if (pl->settings->hideUserlandThreads) { + if (settings->hideUserlandThreads) { pl->totalTasks++; } else { pl->userlandThreads += proc->nlwp; pl->totalTasks += proc->nlwp + 1; } } - proc->show = !(pl->settings->hideKernelThreads && proc->isKernelThread); + proc->show = !(settings->hideKernelThreads && proc->isKernelThread); } else { // We are not in the master LWP, so jump to the LWP handling code proc->percent_cpu = ((uint16_t)_lwpsinfo->pr_pctcpu / (double)32768) * (double)100.0; Process_updateCPUFieldWidths(proc->percent_cpu); @@ -512,10 +232,10 @@ static int SolarisProcessList_walkproc(psinfo_t* _psinfo, lwpsinfo_t* _lwpsinfo, } // Top-level process only gets this for the representative LWP - if (proc->isKernelThread && !pl->settings->hideKernelThreads) { + if (proc->isKernelThread && !settings->hideKernelThreads) { proc->show = true; } - if (!proc->isKernelThread && !pl->settings->hideUserlandThreads) { + if (!proc->isKernelThread && !settings->hideUserlandThreads) { proc->show = true; } } // Top-level LWP or subordinate LWP @@ -540,35 +260,7 @@ static int SolarisProcessList_walkproc(psinfo_t* _psinfo, lwpsinfo_t* _lwpsinfo, return 0; } -void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { - SolarisProcessList_updateCPUcount(super); - SolarisProcessList_scanCPUTime(super); - SolarisProcessList_scanMemoryInfo(super); - SolarisProcessList_scanZfsArcstats(super); - - // in pause mode only gather global data for meters (CPU/memory/...) - if (pauseProcessUpdate) { - return; - } - +void ProcessList_goThroughEntries(ProcessList* super) { super->kernelThreads = 1; proc_walk(&SolarisProcessList_walkproc, super, PR_WALK_LWP); } - -Machine* Machine_new(UsersTable* usersTable, uid_t userId) { - Machine* this = xCalloc(1, sizeof(Machine)); - Machine_init(this, usersTable, userId); - return this; -} - -void Machine_delete(Machine* host) { - free(host); -} - -bool Machine_isCPUonline(const Machine* host, unsigned int id) { - assert(id < host->existingCPUs); - - const SolarisProcessList* spl = (const SolarisProcessList*) host->pl; - - return (super->host->existingCPUs == 1) ? true : spl->cpus[id + 1].online; -} diff --git a/solaris/SolarisProcessList.h b/solaris/SolarisProcessList.h index 7cb641673..d8280117d 100644 --- a/solaris/SolarisProcessList.h +++ b/solaris/SolarisProcessList.h @@ -18,7 +18,6 @@ in the source distribution for its full text. #include #include #include -#include #include "Hashtable.h" #include "ProcessList.h" @@ -26,44 +25,9 @@ in the source distribution for its full text. #include "solaris/SolarisProcess.h" -#include "zfs/ZfsArcStats.h" - - -#define ZONE_ERRMSGLEN 1024 -extern char zone_errmsg[ZONE_ERRMSGLEN]; - -typedef struct CPUData_ { - double userPercent; - double nicePercent; - double systemPercent; - double irqPercent; - double idlePercent; - double systemAllPercent; - double frequency; - uint64_t luser; - uint64_t lkrnl; - uint64_t lintr; - uint64_t lidle; - bool online; -} CPUData; typedef struct SolarisProcessList_ { ProcessList super; - kstat_ctl_t* kd; - CPUData* cpus; - ZfsArcStats zfs; } SolarisProcessList; -ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList); - -void ProcessList_delete(ProcessList* pl); - -void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate); - -Machine* Machine_new(UsersTable* usersTable, uid_t userId); - -bool Machine_isCPUonline(const Machine* host, unsigned int id); - -void Machine_delete(Machine* host); - #endif diff --git a/unsupported/UnsupportedMachine.c b/unsupported/UnsupportedMachine.c new file mode 100644 index 000000000..a6635acc8 --- /dev/null +++ b/unsupported/UnsupportedMachine.c @@ -0,0 +1,56 @@ +/* +htop - UnsupportedMachine.c +(C) 2014 Hisham H. Muhammad +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "UnsupportedMachine.h" + +#include +#include + +#include "Machine.h" + + +Machine* Machine_new(UsersTable* usersTable, uid_t userId) { + UnsupportedMachine* this = xCalloc(1, sizeof(UnsupportedMachine)); + Machine* super = &this->super; + + Machine_init(super, usersTable, userId); + + super->existingCPUs = 1; + super->activeCPUs = 1; + + return super; +} + +void Machine_delete(Machine* super) { + UnsupportedMachine* this = (UnsupportedMachine*) super; + Machine_done(super); + free(this); +} + +bool Machine_isCPUonline(const Machine* host, unsigned int id) { + assert(id < host->existingCPUs); + + (void) host; (void) id; + + return true; +} + +void Machine_scan(Machine* super) { + super->existingCPUs = 1; + super->activeCPUs = 1; + + super->totalMem = 0; + super->usedMem = 0; + super->buffersMem = 0; + super->cachedMem = 0; + super->sharedMem = 0; + super->availableMem = 0; + + super->totalSwap = 0; + super->usedSwap = 0; + super->cachedSwap = 0; +} diff --git a/unsupported/UnsupportedMachine.h b/unsupported/UnsupportedMachine.h new file mode 100644 index 000000000..4ec760f17 --- /dev/null +++ b/unsupported/UnsupportedMachine.h @@ -0,0 +1,17 @@ +#ifndef HEADER_UnsupportedMachine +#define HEADER_UnsupportedMachine +/* +htop - UnsupportedMachine.h +(C) 2014 Hisham H. Muhammad +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "Machine.h" + + +typedef struct UnsupportedMachine_ { + Machine super; +} UnsupportedMachine; + +#endif diff --git a/unsupported/UnsupportedProcessList.c b/unsupported/UnsupportedProcessList.c index e5b7af6d3..e56f49782 100644 --- a/unsupported/UnsupportedProcessList.c +++ b/unsupported/UnsupportedProcessList.c @@ -16,10 +16,8 @@ in the source distribution for its full text. ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList) { ProcessList* this = xCalloc(1, sizeof(ProcessList)); - ProcessList_init(this, Class(Process), host, pidMatchList); - host->existingCPUs = 1; - host->activeCPUs = 1; + ProcessList_init(this, Class(Process), host, pidMatchList); return this; } @@ -29,13 +27,7 @@ void ProcessList_delete(ProcessList* this) { free(this); } -void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { - - // in pause mode only gather global data for meters (CPU/memory/...) - if (pauseProcessUpdate) { - return; - } - +void ProcessList_goThroughEntries(ProcessList* super) { bool preExisting = true; Process* proc; @@ -51,7 +43,8 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { Process_updateCmdline(proc, "", 0, 0); Process_updateExe(proc, "/path/to/executable"); - if (proc->settings->ss->flags & PROCESS_FLAG_CWD) { + const Settings* settings = proc->host->settings; + if (settings->ss->flags & PROCESS_FLAG_CWD) { free_and_xStrdup(&proc->procCwd, "/current/working/directory"); } @@ -60,7 +53,7 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { proc->state = RUNNING; proc->isKernelThread = false; proc->isUserlandThread = false; - proc->show = true; /* Reflected in proc->settings-> "hideXXX" really */ + proc->show = true; /* Reflected in settings-> "hideXXX" really */ proc->pgrp = 0; proc->session = 0; proc->tty_nr = 0; @@ -90,21 +83,3 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { if (!preExisting) ProcessList_add(super, proc); } - -Machine* Machine_new(UsersTable* usersTable, uid_t userId) { - Machine* this = xCalloc(1, sizeof(Machine)); - Machine_init(this, usersTable, userId); - return this; -} - -void Machine_delete(Machine* host) { - free(host); -} - -bool Machine_isCPUonline(const Machine* host, unsigned int id) { - assert(id < host->existingCPUs); - - (void) host; (void) id; - - return true; -} diff --git a/unsupported/UnsupportedProcessList.h b/unsupported/UnsupportedProcessList.h index cdb0b6015..96efdcd2f 100644 --- a/unsupported/UnsupportedProcessList.h +++ b/unsupported/UnsupportedProcessList.h @@ -10,16 +10,8 @@ in the source distribution for its full text. #include "ProcessList.h" -ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList); - -void ProcessList_delete(ProcessList* this); - -void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate); - -Machine* Machine_new(UsersTable* usersTable, uid_t userId); - -bool Machine_isCPUonline(const Machine* host, unsigned int id); - -void Machine_delete(Machine* host); +typedef struct UnsupportedProcessList_ { + ProcessList super; +} UnsupportedProcessList; #endif From 290ddba35e7a1463c1a9fc5ff6bd723cd1670ba2 Mon Sep 17 00:00:00 2001 From: Nathan Scott Date: Mon, 8 May 2023 10:08:20 +1000 Subject: [PATCH 090/106] Minor whitespace and small logic flow improvements on review Quality improvements from BenBE as part of review for #1234. --- darwin/DarwinMachine.c | 16 +++++++--- linux/LinuxMachine.c | 2 +- netbsd/NetBSDMachine.c | 1 + openbsd/Platform.c | 2 +- solaris/Platform.c | 4 +-- solaris/SolarisMachine.c | 69 +++++++++++++++++++++------------------- 6 files changed, 52 insertions(+), 42 deletions(-) diff --git a/darwin/DarwinMachine.c b/darwin/DarwinMachine.c index 6bf52b76a..1290e9597 100644 --- a/darwin/DarwinMachine.c +++ b/darwin/DarwinMachine.c @@ -1,6 +1,7 @@ /* htop - DarwinMachine.c (C) 2014 Hisham H. Muhammad +(C) 2023 htop dev team Released under the GNU GPLv2+, see the COPYING file in the source distribution for its full text. */ @@ -33,12 +34,17 @@ static void DarwinMachine_getHostInfo(host_basic_info_data_t* p) { } static void DarwinMachine_freeCPULoadInfo(processor_cpu_load_info_t* p) { - if (NULL != p && NULL != *p) { - if (0 != munmap(*p, vm_page_size)) { - CRT_fatalError("Unable to free old CPU load information"); - } - *p = NULL; + if (!p) + return; + + if (!*p) + return; + + if (0 != munmap(*p, vm_page_size)) { + CRT_fatalError("Unable to free old CPU load information"); } + + *p = NULL; } static unsigned DarwinMachine_allocateCPULoadInfo(processor_cpu_load_info_t* p) { diff --git a/linux/LinuxMachine.c b/linux/LinuxMachine.c index 48d0e80e1..7b6abfa01 100644 --- a/linux/LinuxMachine.c +++ b/linux/LinuxMachine.c @@ -440,7 +440,7 @@ static void LinuxMachine_scanCPUTime(LinuxMachine* this) { // The rest will remain at zero. unsigned int adjCpuId; if (i == 0) { - (void) sscanf(buffer, "cpu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu", &usertime, &nicetime, &systemtime, &idletime, &ioWait, &irq, &softIrq, &steal, &guest, &guestnice); + (void) sscanf(buffer, "cpu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu", &usertime, &nicetime, &systemtime, &idletime, &ioWait, &irq, &softIrq, &steal, &guest, &guestnice); adjCpuId = 0; } else { unsigned int cpuid; diff --git a/netbsd/NetBSDMachine.c b/netbsd/NetBSDMachine.c index 1e2a0a130..9f3b32de1 100644 --- a/netbsd/NetBSDMachine.c +++ b/netbsd/NetBSDMachine.c @@ -276,6 +276,7 @@ void Machine_scan(Machine* super) { bool Machine_isCPUonline(const Machine* host, unsigned int id) { assert(id < host->existingCPUs); + (void)host; (void)id; // TODO: Support detecting online / offline CPUs. return true; diff --git a/openbsd/Platform.c b/openbsd/Platform.c index 94e71928e..065940892 100644 --- a/openbsd/Platform.c +++ b/openbsd/Platform.c @@ -182,7 +182,7 @@ int Platform_getMaxPid(void) { double Platform_setCPUValues(Meter* this, unsigned int cpu) { const Machine* host = this->host; const OpenBSDMachine* ohost = (const OpenBSDMachine*) host; - const CPUData* cpuData = &(ohost->cpuData[cpu]); + const CPUData* cpuData = &ohost->cpuData[cpu]; double total; double totalPercent; double* v = this->values; diff --git a/solaris/Platform.c b/solaris/Platform.c index ad899492d..95c50b4d7 100644 --- a/solaris/Platform.c +++ b/solaris/Platform.c @@ -258,13 +258,13 @@ void Platform_setSwapValues(Meter* this) { void Platform_setZfsArcValues(Meter* this) { const SolarisMachine* shost = (SolarisMachine*) this->host; - ZfsArcMeter_readStats(this, &(shost->zfs)); + ZfsArcMeter_readStats(this, &shost->zfs); } void Platform_setZfsCompressedArcValues(Meter* this) { const SolarisMachine* shost = (SolarisMachine*) this->host; - ZfsCompressedArcMeter_readStats(this, &(shost->zfs)); + ZfsCompressedArcMeter_readStats(this, &shost->zfs); } static int Platform_buildenv(void* accum, struct ps_prochandle* Phandle, uintptr_t addr, const char* str) { diff --git a/solaris/SolarisMachine.c b/solaris/SolarisMachine.c index 9ce4ced21..b94fc742d 100644 --- a/solaris/SolarisMachine.c +++ b/solaris/SolarisMachine.c @@ -241,48 +241,51 @@ static void SolarisMachine_scanMemoryInfo(SolarisMachine* this) { } static void SolarisMachine_scanZfsArcstats(SolarisMachine* this) { - kstat_named_t *cur_kstat = NULL; - kstat_t *arcstats = NULL; - int ksrphyserr = -1; + kstat_named_t *cur_kstat; + kstat_t *arcstats; + int ksrphyserr; - if (this->kd != NULL) { - arcstats = kstat_lookup_wrapper(this->kd, "zfs", 0, "arcstats"); - } - if (arcstats != NULL) { - ksrphyserr = kstat_read(this->kd, arcstats, NULL); - } - if (ksrphyserr != -1) { - cur_kstat = kstat_data_lookup_wrapper( arcstats, "size" ); - this->zfs.size = cur_kstat->value.ui64 / 1024; - this->zfs.enabled = this->zfs.size > 0 ? 1 : 0; + if (this->kd == NULL) + return; - cur_kstat = kstat_data_lookup_wrapper( arcstats, "c_max" ); - this->zfs.max = cur_kstat->value.ui64 / 1024; + arcstats = kstat_lookup_wrapper(this->kd, "zfs", 0, "arcstats"); + if (arcstats == NULL) + return; - cur_kstat = kstat_data_lookup_wrapper( arcstats, "mfu_size" ); - this->zfs.MFU = cur_kstat != NULL ? cur_kstat->value.ui64 / 1024 : 0; + ksrphyserr = kstat_read(this->kd, arcstats, NULL); + if (ksrphyserr == -1) + return; - cur_kstat = kstat_data_lookup_wrapper( arcstats, "mru_size" ); - this->zfs.MRU = cur_kstat != NULL ? cur_kstat->value.ui64 / 1024 : 0; + cur_kstat = kstat_data_lookup_wrapper( arcstats, "size" ); + this->zfs.size = cur_kstat->value.ui64 / 1024; + this->zfs.enabled = this->zfs.size > 0 ? 1 : 0; - cur_kstat = kstat_data_lookup_wrapper( arcstats, "anon_size" ); - this->zfs.anon = cur_kstat != NULL ? cur_kstat->value.ui64 / 1024 : 0; + cur_kstat = kstat_data_lookup_wrapper( arcstats, "c_max" ); + this->zfs.max = cur_kstat->value.ui64 / 1024; - cur_kstat = kstat_data_lookup_wrapper( arcstats, "hdr_size" ); - this->zfs.header = cur_kstat != NULL ? cur_kstat->value.ui64 / 1024 : 0; + cur_kstat = kstat_data_lookup_wrapper( arcstats, "mfu_size" ); + this->zfs.MFU = cur_kstat != NULL ? cur_kstat->value.ui64 / 1024 : 0; - cur_kstat = kstat_data_lookup_wrapper( arcstats, "other_size" ); - this->zfs.other = cur_kstat != NULL ? cur_kstat->value.ui64 / 1024 : 0; + cur_kstat = kstat_data_lookup_wrapper( arcstats, "mru_size" ); + this->zfs.MRU = cur_kstat != NULL ? cur_kstat->value.ui64 / 1024 : 0; - if ((cur_kstat = kstat_data_lookup_wrapper( arcstats, "compressed_size" )) != NULL) { - this->zfs.compressed = cur_kstat->value.ui64 / 1024; - this->zfs.isCompressed = 1; + cur_kstat = kstat_data_lookup_wrapper( arcstats, "anon_size" ); + this->zfs.anon = cur_kstat != NULL ? cur_kstat->value.ui64 / 1024 : 0; - cur_kstat = kstat_data_lookup_wrapper( arcstats, "uncompressed_size" ); - this->zfs.uncompressed = cur_kstat->value.ui64 / 1024; - } else { - this->zfs.isCompressed = 0; - } + cur_kstat = kstat_data_lookup_wrapper( arcstats, "hdr_size" ); + this->zfs.header = cur_kstat != NULL ? cur_kstat->value.ui64 / 1024 : 0; + + cur_kstat = kstat_data_lookup_wrapper( arcstats, "other_size" ); + this->zfs.other = cur_kstat != NULL ? cur_kstat->value.ui64 / 1024 : 0; + + if ((cur_kstat = kstat_data_lookup_wrapper( arcstats, "compressed_size" )) != NULL) { + this->zfs.compressed = cur_kstat->value.ui64 / 1024; + this->zfs.isCompressed = 1; + + cur_kstat = kstat_data_lookup_wrapper( arcstats, "uncompressed_size" ); + this->zfs.uncompressed = cur_kstat->value.ui64 / 1024; + } else { + this->zfs.isCompressed = 0; } } From ab0f68c20f2e77ef418a19b80222136a1ba885be Mon Sep 17 00:00:00 2001 From: Nathan Scott Date: Tue, 9 May 2023 20:10:20 +1000 Subject: [PATCH 091/106] Build fixes for NetBSD Related to #1238 --- netbsd/NetBSDMachine.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/netbsd/NetBSDMachine.h b/netbsd/NetBSDMachine.h index 57070d675..9c4e75ed2 100644 --- a/netbsd/NetBSDMachine.h +++ b/netbsd/NetBSDMachine.h @@ -40,8 +40,8 @@ typedef struct CPUData_ { double frequency; } CPUData; -typedef struct NetBSDProcessList_ { - ProcessList super; +typedef struct NetBSDMachine_ { + Machine super; kvm_t* kd; long fscale; @@ -49,6 +49,6 @@ typedef struct NetBSDProcessList_ { int pageSizeKB; CPUData* cpuData; -} NetBSDProcessList; +} NetBSDMachine; #endif From e40daf929e8416f8ca272fd286156a0639bb1dbd Mon Sep 17 00:00:00 2001 From: Benny Baumann Date: Tue, 9 May 2023 15:11:11 +0200 Subject: [PATCH 092/106] Build fixes for DragonFlyBSD --- dragonflybsd/DragonFlyBSDMachine.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dragonflybsd/DragonFlyBSDMachine.c b/dragonflybsd/DragonFlyBSDMachine.c index fc7eb3b0d..6831fa3df 100644 --- a/dragonflybsd/DragonFlyBSDMachine.c +++ b/dragonflybsd/DragonFlyBSDMachine.c @@ -45,7 +45,7 @@ Machine* Machine_new(UsersTable* usersTable, uid_t userId) { DragonFlyBSDMachine* this = xCalloc(1, sizeof(DragonFlyBSDMachine)); Machine* super = &this->super; - Machine_init(this, usersTable, userId); + Machine_init(super, usersTable, userId); // physical memory in system: hw.physmem // physical page size: hw.pagesize @@ -119,9 +119,9 @@ Machine* Machine_new(UsersTable* usersTable, uid_t userId) { } void Machine_delete(Machine* super) { - const DragonFlyBSDMachine* this = (DragonFlyBSDProcessList*) super; + const DragonFlyBSDMachine* this = (const DragonFlyBSDMachine*) super; - Machine_done(this); + Machine_done(super); if (this->kd) { kvm_close(this->kd); From 508d9ce5dcda45a33bd31d49cab19566c9cdcc89 Mon Sep 17 00:00:00 2001 From: Ximalas Date: Tue, 9 May 2023 12:48:49 +0200 Subject: [PATCH 093/106] Build fixes for FreeBSD Part of a series of changes to get rid of errors and warnings. --- freebsd/FreeBSDMachine.c | 6 +++--- freebsd/FreeBSDMachine.h | 4 ++-- freebsd/FreeBSDProcessList.c | 11 ++++++----- freebsd/Platform.c | 8 ++++---- 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/freebsd/FreeBSDMachine.c b/freebsd/FreeBSDMachine.c index b8d5d87fb..26da667a2 100644 --- a/freebsd/FreeBSDMachine.c +++ b/freebsd/FreeBSDMachine.c @@ -59,7 +59,7 @@ Machine* Machine_new(UsersTable* usersTable, uid_t userId) { char errbuf[_POSIX2_LINE_MAX]; size_t len; - Machine_init(this, usersTable, userId); + Machine_init(super, usersTable, userId); // physical memory in system: hw.physmem // physical page size: hw.pagesize @@ -146,13 +146,13 @@ Machine* Machine_new(UsersTable* usersTable, uid_t userId) { CRT_fatalError("kvm_openfiles() failed"); } - return this; + return super; } void Machine_delete(Machine* super) { FreeBSDMachine* this = (FreeBSDMachine*) super; - ProcessList_done(this); + Machine_done(super); if (this->kd) { kvm_close(this->kd); diff --git a/freebsd/FreeBSDMachine.h b/freebsd/FreeBSDMachine.h index 1a90534ee..f34b568e6 100644 --- a/freebsd/FreeBSDMachine.h +++ b/freebsd/FreeBSDMachine.h @@ -28,7 +28,7 @@ typedef struct CPUData_ { double temperature; } CPUData; -typedef struct FreeBSDProcessList_ { +typedef struct FreeBSDMachine_ { Machine super; kvm_t* kd; @@ -49,6 +49,6 @@ typedef struct FreeBSDProcessList_ { unsigned long* cp_times_o; unsigned long* cp_times_n; -} FreeBSDProcessList; +} FreeBSDMachine; #endif diff --git a/freebsd/FreeBSDProcessList.c b/freebsd/FreeBSDProcessList.c index 1cf325028..d8d4bbe04 100644 --- a/freebsd/FreeBSDProcessList.c +++ b/freebsd/FreeBSDProcessList.c @@ -29,7 +29,6 @@ in the source distribution for its full text. #include "CRT.h" #include "Compat.h" -#include "FreeBSDProcess.h" #include "Macros.h" #include "Object.h" #include "Process.h" @@ -38,6 +37,9 @@ in the source distribution for its full text. #include "Settings.h" #include "XUtils.h" +#include "freebsd/FreeBSDMachine.h" +#include "freebsd/FreeBSDProcess.h" + ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList) { FreeBSDProcessList* this = xCalloc(1, sizeof(FreeBSDProcessList)); @@ -45,7 +47,7 @@ ProcessList* ProcessList_new(Machine* host, Hashtable* pidMatchList) { ProcessList_init(super, Class(FreeBSDProcess), host, pidMatchList); - return this; + return super; } void ProcessList_delete(ProcessList* super) { @@ -156,14 +158,13 @@ IGNORE_WCASTQUAL_END void ProcessList_goThroughEntries(ProcessList* super) { const Machine* host = super->host; - const FreeBSDMachine* fhost = (FreeBSDMachine*) host; - FreeBSDProcessList* fpl = (FreeBSDProcessList*) super; + const FreeBSDMachine* fhost = (const FreeBSDMachine*) host; const Settings* settings = host->settings; bool hideKernelThreads = settings->hideKernelThreads; bool hideUserlandThreads = settings->hideUserlandThreads; int count = 0; - const struct kinfo_proc* kprocs = kvm_getprocs(fpl->kd, KERN_PROC_PROC, 0, &count); + const struct kinfo_proc* kprocs = kvm_getprocs(fhost->kd, KERN_PROC_PROC, 0, &count); for (int i = 0; i < count; i++) { const struct kinfo_proc* kproc = &kprocs[i]; diff --git a/freebsd/Platform.c b/freebsd/Platform.c index 0588b0fae..a2dc072cd 100644 --- a/freebsd/Platform.c +++ b/freebsd/Platform.c @@ -209,7 +209,7 @@ double Platform_setCPUValues(Meter* this, unsigned int cpu) { v[CPU_METER_NICE] = cpuData->nicePercent; v[CPU_METER_NORMAL] = cpuData->userPercent; - if (super->settings->detailedCPUTime) { + if (host->settings->detailedCPUTime) { v[CPU_METER_KERNEL] = cpuData->systemPercent; v[CPU_METER_IRQ] = cpuData->irqPercent; this->curItems = 4; @@ -240,11 +240,11 @@ void Platform_setMemoryValues(Meter* this) { this->values[MEMORY_METER_CACHE] = host->cachedMem; // this->values[MEMORY_METER_AVAILABLE] = "available memory" - if (dhost->zfs.enabled) { + if (fhost->zfs.enabled) { // ZFS does not shrink below the value of zfs_arc_min. unsigned long long int shrinkableSize = 0; - if (dhost->zfs.size > dhost->zfs.min) - shrinkableSize = dhost->zfs.size - dhost->zfs.min; + if (fhost->zfs.size > fhost->zfs.min) + shrinkableSize = fhost->zfs.size - fhost->zfs.min; this->values[MEMORY_METER_USED] -= shrinkableSize; this->values[MEMORY_METER_CACHE] += shrinkableSize; // this->values[MEMORY_METER_AVAILABLE] += shrinkableSize; From e05a203b9d7832d9a0f244676dba266bee2580d0 Mon Sep 17 00:00:00 2001 From: Benny Baumann Date: Tue, 9 May 2023 15:05:16 +0200 Subject: [PATCH 094/106] Shorten CPUData ptr initialization stanza --- freebsd/Platform.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/freebsd/Platform.c b/freebsd/Platform.c index a2dc072cd..66e1463df 100644 --- a/freebsd/Platform.c +++ b/freebsd/Platform.c @@ -195,14 +195,9 @@ double Platform_setCPUValues(Meter* this, unsigned int cpu) { const Machine* host = this->host; const FreeBSDMachine* fhost = (const FreeBSDMachine*) host; unsigned int cpus = host->activeCPUs; - const CPUData* cpuData; - if (cpus == 1) { - // single CPU box has everything in fhost->cpus[0] - cpuData = &(fhost->cpus[0]); - } else { - cpuData = &(fhost->cpus[cpu]); - } + // single CPU box has everything in fhost->cpus[0] + const CPUData* cpuData = cpus == 1 ? &fhost->cpus[0] : &fhost->cpus[cpu]; double percent; double* v = this->values; From 1f308b1ee756982011a4df0f8d373a98a49669ab Mon Sep 17 00:00:00 2001 From: Benny Baumann Date: Tue, 9 May 2023 15:24:47 +0200 Subject: [PATCH 095/106] Consistency fixes for Darwin --- darwin/DarwinMachine.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/darwin/DarwinMachine.c b/darwin/DarwinMachine.c index 1290e9597..582d49680 100644 --- a/darwin/DarwinMachine.c +++ b/darwin/DarwinMachine.c @@ -101,12 +101,12 @@ Machine* Machine_new(UsersTable* usersTable, uid_t userId) { } void Machine_delete(Machine* super) { - DarwinMachine* host = (DarwinMachine*) super; + DarwinMachine* this = (DarwinMachine*) super; - DarwinMachine_freeCPULoadInfo(&host->prev_load); + DarwinMachine_freeCPULoadInfo(&this->prev_load); Machine_done(super); - free(super); + free(this); } bool Machine_isCPUonline(const Machine* host, unsigned int id) { From 148dfc03ba5154540769ac737dfde9b52b87bb9e Mon Sep 17 00:00:00 2001 From: Benny Baumann Date: Tue, 9 May 2023 15:37:14 +0200 Subject: [PATCH 096/106] Consistency fixes for OpenBSD --- openbsd/OpenBSDProcessList.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openbsd/OpenBSDProcessList.c b/openbsd/OpenBSDProcessList.c index 2d86a9d45..84c833c3c 100644 --- a/openbsd/OpenBSDProcessList.c +++ b/openbsd/OpenBSDProcessList.c @@ -153,7 +153,7 @@ static void OpenBSDProcessList_scanProcs(OpenBSDProcessList* this) { bool preExisting = false; Process* proc = ProcessList_getProcess(&this->super, (kproc->p_tid == -1) ? kproc->p_pid : kproc->p_tid, &preExisting, OpenBSDProcess_new); - OpenBSDProcess* fp = (OpenBSDProcess*) proc; + OpenBSDProcess* op = (OpenBSDProcess*) proc; if (!preExisting) { proc->ppid = kproc->p_ppid; @@ -187,7 +187,7 @@ static void OpenBSDProcessList_scanProcs(OpenBSDProcessList* this) { } } - fp->addr = kproc->p_addr; + op->addr = kproc->p_addr; proc->m_virt = kproc->p_vm_dsize * ohost->pageSizeKB; proc->m_resident = kproc->p_vm_rssize * ohost->pageSizeKB; From a393066ce87b81c2dbe50c1dee3cac8846fcc0cc Mon Sep 17 00:00:00 2001 From: Daniel Lange Date: Tue, 16 May 2023 18:59:01 +0200 Subject: [PATCH 097/106] Update F5 key label on tab switch (Tree <-> List) Closes #1247 --- Action.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Action.c b/Action.c index d6b2a1cea..e00b2c522 100644 --- a/Action.c +++ b/Action.c @@ -352,7 +352,7 @@ static Htop_Reaction actionNextScreen(State* st) { settings->ssIndex = 0; } settings->ss = settings->screens[settings->ssIndex]; - return HTOP_UPDATE_PANELHDR | HTOP_REFRESH; + return HTOP_UPDATE_PANELHDR | HTOP_REFRESH | HTOP_REDRAW_BAR; } static Htop_Reaction actionPrevScreen(State* st) { @@ -363,7 +363,7 @@ static Htop_Reaction actionPrevScreen(State* st) { settings->ssIndex--; } settings->ss = settings->screens[settings->ssIndex]; - return HTOP_UPDATE_PANELHDR | HTOP_REFRESH; + return HTOP_UPDATE_PANELHDR | HTOP_REFRESH | HTOP_REDRAW_BAR; } Htop_Reaction Action_setScreenTab(Settings* settings, int x) { @@ -377,7 +377,7 @@ Htop_Reaction Action_setScreenTab(Settings* settings, int x) { if (x <= s + len + 1) { settings->ssIndex = i; settings->ss = settings->screens[i]; - return HTOP_UPDATE_PANELHDR | HTOP_REFRESH; + return HTOP_UPDATE_PANELHDR | HTOP_REFRESH | HTOP_REDRAW_BAR; } s += len + 3; } From 1de7a2b6e329cc7a9dde366f73d9f0854733cf4e Mon Sep 17 00:00:00 2001 From: Nathan Scott Date: Wed, 17 May 2023 08:19:30 +1000 Subject: [PATCH 098/106] Make Action_pickFromVector code match header prototype Noticed by BenBE in review of #1243 --- Action.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Action.c b/Action.c index e00b2c522..75cf8c853 100644 --- a/Action.c +++ b/Action.c @@ -43,7 +43,7 @@ in the source distribution for its full text. #endif -Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess) { +Object* Action_pickFromVector(State* st, Panel* list, int x, bool follow) { MainPanel* mainPanel = st->mainPanel; Header* header = st->header; Machine* host = st->host; @@ -56,8 +56,8 @@ Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess) Panel* panelFocus; int ch; bool unfollow = false; - int pid = followProcess ? MainPanel_selectedPid(mainPanel) : -1; - if (followProcess && host->pl->following == -1) { + int pid = follow ? MainPanel_selectedPid(mainPanel) : -1; + if (follow && host->pl->following == -1) { host->pl->following = pid; unfollow = true; } @@ -69,7 +69,7 @@ Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess) Panel_move((Panel*)mainPanel, 0, y); Panel_resize((Panel*)mainPanel, COLS, LINES - y - 1); if (panelFocus == list && ch == 13) { - if (followProcess) { + if (follow) { const Process* selected = (const Process*)Panel_getSelected((Panel*)mainPanel); if (selected && selected->pid == pid) return Panel_getSelected(list); From f77ea80b8c6aa65329a66b9bcf3d1fa665fe01cd Mon Sep 17 00:00:00 2001 From: Benny Baumann Date: Mon, 15 May 2023 22:21:09 +0200 Subject: [PATCH 099/106] Minor style guide clarifications --- docs/styleguide.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/docs/styleguide.md b/docs/styleguide.md index 977ee38b4..82198f6b3 100644 --- a/docs/styleguide.md +++ b/docs/styleguide.md @@ -8,12 +8,12 @@ Names are important to convey what all those things inside the project are for. Filenames for source code traditionally used camel-case naming with the first letter written in uppercase. The file extension is always lowercase. -The only exception here is `htop.c` which is the main entrance point into the code. +The only exceptions here are `htop.c` and `pcp-htop.c`, which contain the main entrance points into the code. Folders for e.g. platform-specific code or complex features spawning multiple files are written in lowercase, e.g. `linux`, `freebsd`, `zfs`. Inside files, the naming somewhat depends on the context. -For functions names should include a camel-case prefix before the actual name, separated by an underscore. +Function names should include a camel-case prefix before the actual name, separated by an underscore. While this prefix usually coincides with the module name, this is not required, yet strongly advised. One important exception to this rule are the memory management and the string utility functions from `XUtils.h`. @@ -65,7 +65,7 @@ The list of includes should be the first thing in the file, after the copyright The include list should be in the following order, with each group separated by one blank line: 1. `include "config.h" // IWYU pragma: keep` if the global configuration - from automake&autoconfigure or any of the feature guards for C library headers + from `automake`&`autoconfigure` or any of the feature guards for C library headers (like `__GNU_SOURCE`) are required, optional otherwise. Beware of the IWYU comment. 2. Accompanying module header file (for C source files only, missing inside headers) 3. List of used system headers (non-conditional includes) @@ -97,6 +97,8 @@ Allocation functions assert the amount of memory requested is non-zero. Trying to allocate 0 bytes of memory is an error. Please use the explicit value `NULL` in this case and handle it in your code accordingly. +If the allocated block of memory is intended to hold an array of values, you should use the alternate functions `xReallocArray` and `xReallocArrayZero` instead. + Working with Strings -------------------- @@ -169,7 +171,7 @@ if (fd >= 0) While the existing code base isn't fully consistent with this code style yet it is strongly recommended that new code follows these rules. Adapting surrounding code near places you need to touch is encouraged. -Try to separate such changes into a single, clean-up only commit to reduce noise while reviewing your changes. +Try to split off such changes into a separate, clean-up only commit to reduce noise while reviewing your changes. When writing your code consistency with the surrounding codebase is favoured. @@ -230,6 +232,8 @@ Writing pull-requests (PRs) When writing your PR or patch, the set of patches should contain the minimal changes required. Each patch in itself should ideally be self-contained and runable. +The commit comment should be descriptive (`Updated Foo.c` is not), explain what the changes are and describe why they were made. +While in trivial cases a short subject suffices, more complex changes might warrant a longer description and explanation of the rationale behind the changes. A PR should not contain any merge commits. To follow the upstream branch of your PR rebase your work instead. @@ -239,5 +243,7 @@ Instead squash those changes in the appropriate commit that introduced that mist Git offers `git commit --fixup=` and `git rebase -i --autosquash` to help you with this. Your final PR should contain a minimal set of reasonably sized commits that by themselves are easy to review. +If you open a PR you need to follow up to resolve any comments/change requests. +Otherwise it may be closed without merging. Rebase early. Rebase often. From 3fc286257d3f40733d73722a0ea6b2ad1fd1a550 Mon Sep 17 00:00:00 2001 From: Sahil Siddiq Date: Tue, 7 Feb 2023 22:56:57 +0530 Subject: [PATCH 100/106] Changes in configuration to identify location of 'term.h' * ProvideTerm.h: New file * Update configure.ac to detect term.h * Update iwyu/htop.imp * Add ProvideTerm.h to Makefile Co-authored-by: BenBE --- Makefile.am | 1 + ProvideTerm.h | 24 ++++++++++++++++++++++++ configure.ac | 12 +++++++++++- iwyu/htop.imp | 4 ++++ 4 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 ProvideTerm.h diff --git a/Makefile.am b/Makefile.am index b25d1cb82..e36994c13 100644 --- a/Makefile.am +++ b/Makefile.am @@ -139,6 +139,7 @@ myhtopheaders = \ ProcessList.h \ ProcessLocksScreen.h \ ProvideCurses.h \ + ProvideTerm.h \ RichString.h \ Scheduling.h \ ScreenManager.h \ diff --git a/ProvideTerm.h b/ProvideTerm.h new file mode 100644 index 000000000..0e07b1c49 --- /dev/null +++ b/ProvideTerm.h @@ -0,0 +1,24 @@ +#ifndef HEADER_ProvideTerm +#define HEADER_ProvideTerm +/* +htop - Filename.h +(C) 2023 htop dev team +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "config.h" // IWYU pragma: keep + +// IWYU pragma: begin_exports + +#if defined(HAVE_NCURSESW_TERM_H) +#include +#elif defined(HAVE_NCURSES_TERM_H) +#include +#elif defined(HAVE_TERM_H) +#include +#endif + +// IWYU pragma: end_exports + +#endif // HEADER_ProvideTerm diff --git a/configure.ac b/configure.ac index 36f25b22c..a3cc572f3 100644 --- a/configure.ac +++ b/configure.ac @@ -360,6 +360,11 @@ if test "x$enable_unicode" = xyes; then [AC_CHECK_HEADERS([ncurses.h], [], [AC_MSG_ERROR([can not find required ncurses header file])])])])]) + AC_CHECK_HEADERS([ncursesw/term.h], [], + [AC_CHECK_HEADERS([ncurses/term.h], [], + [AC_CHECK_HEADERS([term.h], [], + [AC_MSG_ERROR([can not find required term header file])])])]) + # check if additional linker flags are needed for keypad(3) # (at this point we already link against a working ncurses library with wide character support) AC_SEARCH_LIBS([keypad], [tinfow tinfo]) @@ -375,13 +380,18 @@ else AC_CHECK_HEADERS([curses.h], [], [AC_CHECK_HEADERS([ncurses/curses.h], [], [AC_CHECK_HEADERS([ncurses/ncurses.h], [], - [AC_CHECK_HEADERS([ncurses.h] ,[], + [AC_CHECK_HEADERS([ncurses.h], [], [AC_MSG_ERROR([can not find required ncurses header file])])])])]) + AC_CHECK_HEADERS([ncurses/term.h], [], + [AC_CHECK_HEADERS([term.h], [], + [AC_MSG_ERROR([can not find required term header file])])]) + # check if additional linker flags are needed for keypad(3) # (at this point we already link against a working ncurses library) AC_SEARCH_LIBS([keypad], [tinfo]) fi + if test "$enable_static" = yes; then AC_SEARCH_LIBS([Gpm_GetEvent], [gpm]) fi diff --git a/iwyu/htop.imp b/iwyu/htop.imp index 1416d7433..5e87cdbff 100644 --- a/iwyu/htop.imp +++ b/iwyu/htop.imp @@ -4,6 +4,10 @@ { include: ["", "private", "\"ProvideCurses.h\"", "public"] }, { include: ["", "private", "\"ProvideCurses.h\"", "public"] }, + { include: ["", "private", "\"ProvideTerm.h\"", "public"] }, + { include: ["", "private", "\"ProvideTerm.h\"", "public"] }, + { include: ["", "private", "\"ProvideTerm.h\"", "public"] }, + { include: ["", "private", "", "public"] }, { include: ["", "private", "", "public"] }, From 87db37966a8969a931fd49ebc2f9709bd27290d9 Mon Sep 17 00:00:00 2001 From: Sahil Siddiq Date: Tue, 31 Jan 2023 21:20:57 +0530 Subject: [PATCH 101/106] Implement logic for '--max-iterations' * Add '--max-iterations' option to longopts and shortopts * Implement termination logic * Add iterationsRemaining to 'struct Machine' Co-authored-by: BenBE --- CommandLine.c | 20 ++++++++++++++++++-- Machine.h | 2 ++ ScreenManager.c | 6 ++++++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/CommandLine.c b/CommandLine.c index 8095fa8f8..0f168fb07 100644 --- a/CommandLine.c +++ b/CommandLine.c @@ -57,7 +57,8 @@ static void printHelpFlag(const char* name) { #ifdef HAVE_GETMOUSE printf("-M --no-mouse Disable the mouse\n"); #endif - printf("-p --pid=PID[,PID,PID...] Show only the given PIDs\n" + printf("-n --max-iterations=NUMBER Exit htop after NUMBER iterations/frame updates\n" + "-p --pid=PID[,PID,PID...] Show only the given PIDs\n" " --readonly Disable all system and process changing features\n" "-s --sort-key=COLUMN Sort by COLUMN in list view (try --sort-key=help for a list)\n" "-t --tree Show the tree view (can be combined with -s)\n" @@ -78,6 +79,7 @@ typedef struct CommandLineSettings_ { uid_t userId; int sortKey; int delay; + int iterationsRemaining; bool useColors; #ifdef HAVE_GETMOUSE bool enableMouse; @@ -97,6 +99,7 @@ static CommandLineStatus parseArguments(int argc, char** argv, CommandLineSettin .userId = (uid_t)-1, // -1 is guaranteed to be an invalid uid_t (see setreuid(2)) .sortKey = 0, .delay = -1, + .iterationsRemaining = -1, .useColors = true, #ifdef HAVE_GETMOUSE .enableMouse = true, @@ -113,6 +116,7 @@ static CommandLineStatus parseArguments(int argc, char** argv, CommandLineSettin {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'V'}, {"delay", required_argument, 0, 'd'}, + {"max-iterations", required_argument, 0, 'n'}, {"sort-key", required_argument, 0, 's'}, {"user", optional_argument, 0, 'u'}, {"no-color", no_argument, 0, 'C'}, @@ -130,7 +134,7 @@ static CommandLineStatus parseArguments(int argc, char** argv, CommandLineSettin int opt, opti = 0; /* Parse arguments */ - while ((opt = getopt_long(argc, argv, "hVMCs:td:u::Up:F:H::", long_opts, &opti))) { + while ((opt = getopt_long(argc, argv, "hVMCs:td:n:u::Up:F:H::", long_opts, &opti))) { if (opt == EOF) break; switch (opt) { @@ -176,6 +180,17 @@ static CommandLineStatus parseArguments(int argc, char** argv, CommandLineSettin return STATUS_ERROR_EXIT; } break; + case 'n': + if (sscanf(optarg, "%16d", &flags->iterationsRemaining) == 1) { + if (flags->iterationsRemaining <= 0) { + fprintf(stderr, "Error: maximum iteration count must be positive.\n"); + return STATUS_ERROR_EXIT; + } + } else { + fprintf(stderr, "Error: invalid maximum iteration count \"%s\".\n", optarg); + return STATUS_ERROR_EXIT; + } + break; case 'u': { const char* username = optarg; @@ -360,6 +375,7 @@ int CommandLine_run(int argc, char** argv) { ScreenSettings_setSortKey(settings->ss, flags.sortKey); } + host->iterationsRemaining = flags.iterationsRemaining; CRT_init(settings, flags.allowUnicode); MainPanel* panel = MainPanel_new(); diff --git a/Machine.h b/Machine.h index 4628d686c..64d6f6c29 100644 --- a/Machine.h +++ b/Machine.h @@ -46,6 +46,8 @@ typedef struct Machine_ { uint64_t realtimeMs; /* current time in milliseconds */ uint64_t monotonicMs; /* same, but from monotonic clock */ + int64_t iterationsRemaining; + #ifdef HAVE_LIBHWLOC hwloc_topology_t topology; bool topologyOk; diff --git a/ScreenManager.c b/ScreenManager.c index 18e09343e..a089eda10 100644 --- a/ScreenManager.c +++ b/ScreenManager.c @@ -246,6 +246,12 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey, con if (redraw || force_redraw) { ScreenManager_drawPanels(this, focus, force_redraw); force_redraw = false; + if (this->host->iterationsRemaining != -1) { + if (!--this->host->iterationsRemaining) { + quit = true; + continue; + } + } } int prevCh = ch; From dc883b2910f1feea7806fa5109916f9e1ae0d030 Mon Sep 17 00:00:00 2001 From: Sahil Siddiq Date: Mon, 22 May 2023 11:09:11 +0530 Subject: [PATCH 102/106] Logic to retain screen and other changes * Retain htop screen after quitting * Pass additional flag to CRT_init() --- CRT.c | 20 +++++++++++++++++++- CRT.h | 2 +- CommandLine.c | 2 +- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/CRT.c b/CRT.c index a82fc8c1b..1846d4e5e 100644 --- a/CRT.c +++ b/CRT.c @@ -21,6 +21,7 @@ in the source distribution for its full text. #include "CommandLine.h" #include "ProvideCurses.h" +#include "ProvideTerm.h" #include "XUtils.h" #if !defined(NDEBUG) && defined(HAVE_MEMFD_CREATE) @@ -793,6 +794,8 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [COLORSCHEME_BROKENGRAY] = { 0 } // dynamically generated. }; +static bool CRT_retainScreenOnExit = false; + int CRT_scrollHAmount = 5; int CRT_scrollWheelVAmount = 10; @@ -951,8 +954,19 @@ void CRT_setMouse(bool enabled) { } #endif -void CRT_init(const Settings* settings, bool allowUnicode) { +void CRT_init(const Settings* settings, bool allowUnicode, bool retainScreenOnExit) { initscr(); + + if (retainScreenOnExit) { + CRT_retainScreenOnExit = true; + refresh(); + tputs(exit_ca_mode, 0, putchar); + tputs(clear_screen, 0, putchar); + fflush(stdout); + enter_ca_mode = 0; + exit_ca_mode = 0; + } + redirectStderr(); noecho(); CRT_crashSettings = settings; @@ -1056,6 +1070,10 @@ void CRT_done(void) { attroff(resetColor); refresh(); + if (CRT_retainScreenOnExit) { + mvcur(-1, -1, LINES - 1, 0); + } + curs_set(1); endwin(); diff --git a/CRT.h b/CRT.h index ae78c9c50..f5fd94527 100644 --- a/CRT.h +++ b/CRT.h @@ -197,7 +197,7 @@ void CRT_setMouse(bool enabled); #define CRT_setMouse(enabled) #endif -void CRT_init(const Settings* settings, bool allowUnicode); +void CRT_init(const Settings* settings, bool allowUnicode, bool retainScreenOnExit); void CRT_done(void); diff --git a/CommandLine.c b/CommandLine.c index 0f168fb07..3d1f953fc 100644 --- a/CommandLine.c +++ b/CommandLine.c @@ -376,7 +376,7 @@ int CommandLine_run(int argc, char** argv) { } host->iterationsRemaining = flags.iterationsRemaining; - CRT_init(settings, flags.allowUnicode); + CRT_init(settings, flags.allowUnicode, flags.iterationsRemaining != -1); MainPanel* panel = MainPanel_new(); ProcessList_setPanel(pl, (Panel*) panel); From 4227fbd06ebe113daf4b238484cedbf3d52de31c Mon Sep 17 00:00:00 2001 From: Sahil Siddiq Date: Tue, 23 May 2023 19:50:49 +0530 Subject: [PATCH 103/106] Document 'O' keyboard shortcut Co-authored-by: BenBE --- Action.c | 5 +++-- htop.1.in | 4 ++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Action.c b/Action.c index 75cf8c853..62308da72 100644 --- a/Action.c +++ b/Action.c @@ -606,6 +606,7 @@ static const struct { { .key = " u: ", .roInactive = false, .info = "show processes of a single user" }, { .key = " H: ", .roInactive = false, .info = "hide/show user process threads" }, { .key = " K: ", .roInactive = false, .info = "hide/show kernel threads" }, + { .key = " O: ", .roInactive = false, .info = "hide/show processes in containers" }, { .key = " F: ", .roInactive = false, .info = "cursor follows process" }, { .key = " + - *: ", .roInactive = false, .info = "expand/collapse tree/toggle all" }, { .key = "N P M T: ", .roInactive = false, .info = "sort by PID, CPU%, MEM% or TIME" }, @@ -763,9 +764,9 @@ static Htop_Reaction actionHelp(State* st) { for (item = 0; helpRight[item].key; item++) { attrset((helpRight[item].roInactive && readonly) ? CRT_colors[HELP_SHADOW] : CRT_colors[HELP_BOLD]); - mvaddstr(line + item, 41, helpRight[item].key); + mvaddstr(line + item, 43, helpRight[item].key); attrset((helpRight[item].roInactive && readonly) ? CRT_colors[HELP_SHADOW] : CRT_colors[DEFAULT_COLOR]); - mvaddstr(line + item, 50, helpRight[item].info); + mvaddstr(line + item, 52, helpRight[item].info); } line += MAXIMUM(leftHelpItems, item); line++; diff --git a/htop.1.in b/htop.1.in index 72a98fbe8..18be08dc6 100644 --- a/htop.1.in +++ b/htop.1.in @@ -258,6 +258,10 @@ Hide user threads: on systems that represent them differently than ordinary processes (such as recent NPTL-based systems), this can hide threads from userspace processes in the process list. (This is a toggle key.) .TP +.B O +Hide containerized processes: prevent processes running in a container +from being displayed in the process list. (This is a toggle key.) +.TP .B p Show full paths to running programs, where applicable. (This is a toggle key.) .TP From b810678a8dcb708b452864f5991f007920161bdb Mon Sep 17 00:00:00 2001 From: Benny Baumann Date: Sun, 28 May 2023 23:43:52 +0200 Subject: [PATCH 104/106] Avoid comma operator to reduce ambiguity of the assignment --- RichString.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RichString.h b/RichString.h index cbcbe48b7..9a09166c2 100644 --- a/RichString.h +++ b/RichString.h @@ -18,7 +18,7 @@ in the source distribution for its full text. #define RichString_begin(this) RichString this; RichString_beginAllocated(this) #define RichString_beginAllocated(this) \ do { \ - (this).chlen = 0, \ + (this).chlen = 0; \ (this).chptr = (this).chstr; \ RichString_setChar(&(this), 0, 0); \ (this).highlightAttr = 0; \ From 0fb0d75433191ce8056ff1b32ae371f4c4b0a6e0 Mon Sep 17 00:00:00 2001 From: Nathan Scott Date: Mon, 5 Jun 2023 10:04:05 +1000 Subject: [PATCH 105/106] Fix Linux running tasks count in the Tasks Meter, a recent regression The recent split of machine-wide metric gathering from process metrics gathering introduced a regression in the Linux running task accounting. The way we extract this value from /proc/stat (which typically is used for machine-wide metrics) conspired with a now-incorrectly placed init- to-zero at the start of the per-process sampling, caused this value to always be zero by the time the Tasks Meter used it. Fix this by making it much more clear that Linux has this special case handling of runningTasks. Resolves #1256 --- linux/LinuxMachine.c | 2 +- linux/LinuxMachine.h | 5 ++++- linux/LinuxProcessList.c | 3 +++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/linux/LinuxMachine.c b/linux/LinuxMachine.c index 7b6abfa01..68b731850 100644 --- a/linux/LinuxMachine.c +++ b/linux/LinuxMachine.c @@ -501,7 +501,7 @@ static void LinuxMachine_scanCPUTime(LinuxMachine* this) { char buffer[PROC_LINE_LENGTH + 1]; while (fgets(buffer, sizeof(buffer), file)) { if (String_startsWith(buffer, "procs_running")) { - super->pl->runningTasks = strtoul(buffer + strlen("procs_running"), NULL, 10); + this->runningTasks = strtoul(buffer + strlen("procs_running"), NULL, 10); break; } } diff --git a/linux/LinuxMachine.h b/linux/LinuxMachine.h index 7dba905fd..c3076a304 100644 --- a/linux/LinuxMachine.h +++ b/linux/LinuxMachine.h @@ -61,10 +61,13 @@ typedef struct LinuxMachine_ { Machine super; long jiffies; - long long boottime; int pageSize; int pageSizeKB; + /* see Linux kernel source for further detail, fs/proc/stat.c */ + unsigned int runningTasks; /* procs_running from /proc/stat */ + long long boottime; /* btime field from /proc/stat */ + double period; CPUData* cpuData; diff --git a/linux/LinuxProcessList.c b/linux/LinuxProcessList.c index e0a33e287..cd3a2607b 100644 --- a/linux/LinuxProcessList.c +++ b/linux/LinuxProcessList.c @@ -1312,6 +1312,9 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ const ScreenSettings* ss = settings->ss; const struct dirent* entry; + /* set runningTasks from /proc/stat (from Machine_scanCPUTime) */ + pl->runningTasks = lhost->runningTasks; + #ifdef HAVE_OPENAT int dirFd = openat(parentFd, dirname, O_RDONLY | O_DIRECTORY | O_NOFOLLOW); if (dirFd < 0) From d8fe027875427be6f5e598fc82373ecb5ed84859 Mon Sep 17 00:00:00 2001 From: Nathan Scott Date: Tue, 6 Jun 2023 09:18:06 +1000 Subject: [PATCH 106/106] Remove duplicate zeroing of global task accounting values on MacOS This has already been performed in ProcessList.c before calling the platform-specific code, so this is a no-op. --- darwin/DarwinProcessList.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/darwin/DarwinProcessList.c b/darwin/DarwinProcessList.c index bf311dc7a..c10e772c9 100644 --- a/darwin/DarwinProcessList.c +++ b/darwin/DarwinProcessList.c @@ -86,12 +86,6 @@ void ProcessList_goThroughEntries(ProcessList* super) { const double time_interval_ns = Platform_schedulerTicksToNanoseconds(dpl->global_diff) / (double) host->activeCPUs; - /* Clear the thread counts */ - super->kernelThreads = 0; - super->userlandThreads = 0; - super->totalTasks = 0; - super->runningTasks = 0; - /* We use kinfo_procs for initial data since : * * 1) They always succeed.