mirror of
https://github.com/xzeldon/htop.git
synced 2025-07-15 05:24:36 +03:00
Compare commits
59 Commits
Author | SHA1 | Date | |
---|---|---|---|
691b7dd5af | |||
ad0f9c58bf | |||
a8d0f2a7fe | |||
e6596cbd69 | |||
40c9f89971 | |||
09ad8067f4 | |||
bda3ee2b81 | |||
bdb015ffa3 | |||
80a515abcc | |||
5000cefc13 | |||
bf7d98e7ac | |||
c096712b8d | |||
be82448bd5 | |||
68c00b9cdb | |||
3c8d586a1c | |||
3869c43393 | |||
fcca4c2f2d | |||
dd88510dcd | |||
43ffdb0eda | |||
bf395e10c5 | |||
8f259bc5e1 | |||
38e6136b82 | |||
589b0733d9 | |||
c9b58c7fbe | |||
ca06e68037 | |||
a516e0852e | |||
3f805cf347 | |||
556d7c03e8 | |||
0925c54caa | |||
d0f5b61aa5 | |||
284f8c5e0b | |||
11b65a2861 | |||
b85b718e69 | |||
7891cdc552 | |||
b9fdf1c2a1 | |||
2844429f15 | |||
3834f2a68f | |||
343c2e58be | |||
fd2c4f5ddd | |||
7f95ed8528 | |||
9579d9b7aa | |||
0580dbb202 | |||
c0c2bb98a2 | |||
9b30870eec | |||
25c945e2ef | |||
f94934472f | |||
becd33795c | |||
74f99e3693 | |||
e1f4645bd1 | |||
0afd0fe572 | |||
ff336b652c | |||
58a59c11f4 | |||
3f806368e0 | |||
4855d92469 | |||
b81bb9038c | |||
1f2f567ca1 | |||
393330239e | |||
d2c34259b4 | |||
858ad8029d |
@ -9,7 +9,8 @@ os:
|
||||
|
||||
script:
|
||||
- ./autogen.sh
|
||||
# clang might warn about C11 generic selections in isnan()
|
||||
- CFLAGS=-Wno-c11-extensions ./configure --enable-werror
|
||||
- ./configure --enable-werror
|
||||
- make -k
|
||||
- CFLAGS=-Wno-c11-extensions make distcheck DISTCHECK_CONFIGURE_FLAGS=--enable-werror
|
||||
- make distcheck DISTCHECK_CONFIGURE_FLAGS=--enable-werror
|
||||
- sudo make install
|
||||
- make installcheck
|
||||
|
@ -63,9 +63,9 @@ Affinity* Affinity_get(const Process* proc, ProcessList* pl) {
|
||||
Affinity_add(affinity, i);
|
||||
}
|
||||
} else {
|
||||
unsigned int id;
|
||||
hwloc_bitmap_foreach_begin(id, cpuset);
|
||||
Affinity_add(affinity, id);
|
||||
int id;
|
||||
hwloc_bitmap_foreach_begin(id, cpuset)
|
||||
Affinity_add(affinity, (unsigned)id);
|
||||
hwloc_bitmap_foreach_end();
|
||||
}
|
||||
}
|
||||
|
@ -425,9 +425,9 @@ Affinity* AffinityPanel_getAffinity(Panel* super, ProcessList* pl) {
|
||||
Affinity* affinity = Affinity_new(pl);
|
||||
|
||||
#ifdef HAVE_LIBHWLOC
|
||||
unsigned int i;
|
||||
int i;
|
||||
hwloc_bitmap_foreach_begin(i, this->workCpuset)
|
||||
Affinity_add(affinity, i);
|
||||
Affinity_add(affinity, (unsigned)i);
|
||||
hwloc_bitmap_foreach_end();
|
||||
#else
|
||||
for (int i = 0; i < Vector_size(this->cpuids); i++) {
|
||||
|
15
CPUMeter.c
15
CPUMeter.c
@ -52,9 +52,9 @@ static void CPUMeter_init(Meter* this) {
|
||||
// Custom uiName runtime logic to include the param (processor)
|
||||
static void CPUMeter_getUiName(const Meter* this, char* buffer, size_t length) {
|
||||
if (this->param > 0)
|
||||
xSnprintf(buffer, sizeof(length), "%s %u", Meter_uiName(this), this->param);
|
||||
xSnprintf(buffer, length, "%s %u", Meter_uiName(this), this->param);
|
||||
else
|
||||
xSnprintf(buffer, sizeof(length), "%s", Meter_uiName(this));
|
||||
xSnprintf(buffer, length, "%s", Meter_uiName(this));
|
||||
}
|
||||
|
||||
static void CPUMeter_updateValues(Meter* this) {
|
||||
@ -381,6 +381,7 @@ const MeterClass AllCPUs2Meter_class = {
|
||||
},
|
||||
.updateValues = AllCPUsMeter_updateValues,
|
||||
.defaultMode = CUSTOM_METERMODE,
|
||||
.isMultiColumn = true,
|
||||
.total = 100.0,
|
||||
.attributes = CPUMeter_attributes,
|
||||
.name = "AllCPUs2",
|
||||
@ -401,6 +402,7 @@ const MeterClass LeftCPUsMeter_class = {
|
||||
},
|
||||
.updateValues = AllCPUsMeter_updateValues,
|
||||
.defaultMode = CUSTOM_METERMODE,
|
||||
.isMultiColumn = true,
|
||||
.total = 100.0,
|
||||
.attributes = CPUMeter_attributes,
|
||||
.name = "LeftCPUs",
|
||||
@ -421,6 +423,7 @@ const MeterClass RightCPUsMeter_class = {
|
||||
},
|
||||
.updateValues = AllCPUsMeter_updateValues,
|
||||
.defaultMode = CUSTOM_METERMODE,
|
||||
.isMultiColumn = true,
|
||||
.total = 100.0,
|
||||
.attributes = CPUMeter_attributes,
|
||||
.name = "RightCPUs",
|
||||
@ -441,6 +444,7 @@ const MeterClass LeftCPUs2Meter_class = {
|
||||
},
|
||||
.updateValues = AllCPUsMeter_updateValues,
|
||||
.defaultMode = CUSTOM_METERMODE,
|
||||
.isMultiColumn = true,
|
||||
.total = 100.0,
|
||||
.attributes = CPUMeter_attributes,
|
||||
.name = "LeftCPUs2",
|
||||
@ -461,6 +465,7 @@ const MeterClass RightCPUs2Meter_class = {
|
||||
},
|
||||
.updateValues = AllCPUsMeter_updateValues,
|
||||
.defaultMode = CUSTOM_METERMODE,
|
||||
.isMultiColumn = true,
|
||||
.total = 100.0,
|
||||
.attributes = CPUMeter_attributes,
|
||||
.name = "RightCPUs2",
|
||||
@ -481,6 +486,7 @@ const MeterClass AllCPUs4Meter_class = {
|
||||
},
|
||||
.updateValues = AllCPUsMeter_updateValues,
|
||||
.defaultMode = CUSTOM_METERMODE,
|
||||
.isMultiColumn = true,
|
||||
.total = 100.0,
|
||||
.attributes = CPUMeter_attributes,
|
||||
.name = "AllCPUs4",
|
||||
@ -501,6 +507,7 @@ const MeterClass LeftCPUs4Meter_class = {
|
||||
},
|
||||
.updateValues = AllCPUsMeter_updateValues,
|
||||
.defaultMode = CUSTOM_METERMODE,
|
||||
.isMultiColumn = true,
|
||||
.total = 100.0,
|
||||
.attributes = CPUMeter_attributes,
|
||||
.name = "LeftCPUs4",
|
||||
@ -521,6 +528,7 @@ const MeterClass RightCPUs4Meter_class = {
|
||||
},
|
||||
.updateValues = AllCPUsMeter_updateValues,
|
||||
.defaultMode = CUSTOM_METERMODE,
|
||||
.isMultiColumn = true,
|
||||
.total = 100.0,
|
||||
.attributes = CPUMeter_attributes,
|
||||
.name = "RightCPUs4",
|
||||
@ -541,6 +549,7 @@ const MeterClass AllCPUs8Meter_class = {
|
||||
},
|
||||
.updateValues = AllCPUsMeter_updateValues,
|
||||
.defaultMode = CUSTOM_METERMODE,
|
||||
.isMultiColumn = true,
|
||||
.total = 100.0,
|
||||
.attributes = CPUMeter_attributes,
|
||||
.name = "AllCPUs8",
|
||||
@ -561,6 +570,7 @@ const MeterClass LeftCPUs8Meter_class = {
|
||||
},
|
||||
.updateValues = AllCPUsMeter_updateValues,
|
||||
.defaultMode = CUSTOM_METERMODE,
|
||||
.isMultiColumn = true,
|
||||
.total = 100.0,
|
||||
.attributes = CPUMeter_attributes,
|
||||
.name = "LeftCPUs8",
|
||||
@ -581,6 +591,7 @@ const MeterClass RightCPUs8Meter_class = {
|
||||
},
|
||||
.updateValues = AllCPUsMeter_updateValues,
|
||||
.defaultMode = CUSTOM_METERMODE,
|
||||
.isMultiColumn = true,
|
||||
.total = 100.0,
|
||||
.attributes = CPUMeter_attributes,
|
||||
.name = "RightCPUs8",
|
||||
|
22
CRT.c
22
CRT.c
@ -138,7 +138,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
|
||||
[PROCESS_GIGABYTES] = ColorPair(Green, Black),
|
||||
[PROCESS_BASENAME] = A_BOLD | ColorPair(Cyan, Black),
|
||||
[PROCESS_TREE] = ColorPair(Cyan, Black),
|
||||
[PROCESS_R_STATE] = ColorPair(Green, Black),
|
||||
[PROCESS_RUN_STATE] = ColorPair(Green, Black),
|
||||
[PROCESS_D_STATE] = A_BOLD | ColorPair(Red, Black),
|
||||
[PROCESS_HIGH_PRIORITY] = ColorPair(Red, Black),
|
||||
[PROCESS_LOW_PRIORITY] = ColorPair(Green, Black),
|
||||
@ -240,7 +240,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
|
||||
[PROCESS_GIGABYTES] = A_BOLD,
|
||||
[PROCESS_BASENAME] = A_BOLD,
|
||||
[PROCESS_TREE] = A_BOLD,
|
||||
[PROCESS_R_STATE] = A_BOLD,
|
||||
[PROCESS_RUN_STATE] = A_BOLD,
|
||||
[PROCESS_D_STATE] = A_BOLD,
|
||||
[PROCESS_HIGH_PRIORITY] = A_BOLD,
|
||||
[PROCESS_LOW_PRIORITY] = A_DIM,
|
||||
@ -342,7 +342,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
|
||||
[PROCESS_GIGABYTES] = ColorPair(Green, White),
|
||||
[PROCESS_BASENAME] = ColorPair(Blue, White),
|
||||
[PROCESS_TREE] = ColorPair(Green, White),
|
||||
[PROCESS_R_STATE] = ColorPair(Green, White),
|
||||
[PROCESS_RUN_STATE] = ColorPair(Green, White),
|
||||
[PROCESS_D_STATE] = A_BOLD | ColorPair(Red, White),
|
||||
[PROCESS_HIGH_PRIORITY] = ColorPair(Red, White),
|
||||
[PROCESS_LOW_PRIORITY] = ColorPair(Green, White),
|
||||
@ -444,7 +444,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
|
||||
[PROCESS_GIGABYTES] = ColorPair(Green, Black),
|
||||
[PROCESS_BASENAME] = ColorPair(Green, Black),
|
||||
[PROCESS_TREE] = ColorPair(Blue, Black),
|
||||
[PROCESS_R_STATE] = ColorPair(Green, Black),
|
||||
[PROCESS_RUN_STATE] = ColorPair(Green, Black),
|
||||
[PROCESS_D_STATE] = A_BOLD | ColorPair(Red, Black),
|
||||
[PROCESS_HIGH_PRIORITY] = ColorPair(Red, Black),
|
||||
[PROCESS_LOW_PRIORITY] = ColorPair(Green, Black),
|
||||
@ -546,7 +546,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
|
||||
[PROCESS_GIGABYTES] = ColorPair(Green, Blue),
|
||||
[PROCESS_BASENAME] = A_BOLD | ColorPair(Cyan, Blue),
|
||||
[PROCESS_TREE] = ColorPair(Cyan, Blue),
|
||||
[PROCESS_R_STATE] = ColorPair(Green, Blue),
|
||||
[PROCESS_RUN_STATE] = ColorPair(Green, Blue),
|
||||
[PROCESS_D_STATE] = A_BOLD | ColorPair(Red, Blue),
|
||||
[PROCESS_HIGH_PRIORITY] = ColorPair(Red, Blue),
|
||||
[PROCESS_LOW_PRIORITY] = ColorPair(Green, Blue),
|
||||
@ -652,7 +652,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_R_STATE] = ColorPair(Green, Black),
|
||||
[PROCESS_RUN_STATE] = ColorPair(Green, Black),
|
||||
[PROCESS_D_STATE] = A_BOLD | ColorPair(Red, Black),
|
||||
[PROCESS_HIGH_PRIORITY] = ColorPair(Red, Black),
|
||||
[PROCESS_LOW_PRIORITY] = ColorPair(Green, Black),
|
||||
@ -772,6 +772,8 @@ static void dumpStderr(void) {
|
||||
|
||||
fsync(STDERR_FILENO);
|
||||
dup2(stderrRedirectBackupFd, STDERR_FILENO);
|
||||
close(stderrRedirectBackupFd);
|
||||
stderrRedirectBackupFd = -1;
|
||||
lseek(stderrRedirectNewFd, 0, SEEK_SET);
|
||||
|
||||
bool header = false;
|
||||
@ -886,6 +888,10 @@ void CRT_init(const Settings* settings, bool allowUnicode) {
|
||||
}
|
||||
|
||||
if (termType && (String_startsWith(termType, "xterm") || String_eq(termType, "vt220"))) {
|
||||
#ifdef HTOP_NETBSD
|
||||
#define define_key(s_, k_) define_key((char*)s_, k_)
|
||||
IGNORE_WCASTQUAL_BEGIN
|
||||
#endif
|
||||
define_key("\033[H", KEY_HOME);
|
||||
define_key("\033[F", KEY_END);
|
||||
define_key("\033[7~", KEY_HOME);
|
||||
@ -906,6 +912,10 @@ void CRT_init(const Settings* settings, bool allowUnicode) {
|
||||
sequence[1] = c;
|
||||
define_key(sequence, KEY_ALT('A' + (c - 'a')));
|
||||
}
|
||||
#ifdef HTOP_NETBSD
|
||||
IGNORE_WCASTQUAL_END
|
||||
#undef define_key
|
||||
#endif
|
||||
}
|
||||
|
||||
CRT_installSignalHandlers();
|
||||
|
2
CRT.h
2
CRT.h
@ -74,7 +74,7 @@ typedef enum ColorElements_ {
|
||||
PROCESS_MEGABYTES,
|
||||
PROCESS_GIGABYTES,
|
||||
PROCESS_TREE,
|
||||
PROCESS_R_STATE,
|
||||
PROCESS_RUN_STATE,
|
||||
PROCESS_D_STATE,
|
||||
PROCESS_BASENAME,
|
||||
PROCESS_HIGH_PRIORITY,
|
||||
|
@ -40,7 +40,7 @@ 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));
|
||||
MetersPanel** meterPanels = xMallocArray(columns, sizeof(MetersPanel*));
|
||||
|
||||
for (size_t i = 0; i < columns; i++) {
|
||||
char titleBuffer[32];
|
||||
|
18
ChangeLog
18
ChangeLog
@ -1,6 +1,5 @@
|
||||
What's new in version 3.1.0
|
||||
|
||||
|
||||
* Updated COPYING file to remove the PLPA exemption (appendix 2)
|
||||
With this change the license is now GPLv2 without any additional wording.
|
||||
* Improved default sort ordering
|
||||
@ -26,10 +25,11 @@ What's new in version 3.1.0
|
||||
Moving this column to be handled as a platform-independently available
|
||||
information simplifies the markup of the command line.
|
||||
* Introduce configuration file versioning and config_reader_min_version
|
||||
Starting with this version the configuration file contains an version
|
||||
Starting with this version the configuration file contains a version
|
||||
identifying the minimum version of the configuration parser needed to
|
||||
fully understand the configuration file format.
|
||||
Old configuration file formats are automatically upgraded on startup.
|
||||
Old configuration file formats are automatically upgraded when
|
||||
saving the config file (htoprc).
|
||||
* Make the configuration parser friendlier to users (thanks to Bart Bakker)
|
||||
With this change only settings that cannot be parsed properly are
|
||||
reset to their defaults.
|
||||
@ -48,12 +48,12 @@ What's new in version 3.1.0
|
||||
* Option and key ("*") to collapse / expand all branches under PID 1
|
||||
(and PID 2 if kernel threads are shown) (thanks to Krishna Chaitanya)
|
||||
* Keep following a process when inverting the sort order, displaying
|
||||
the help screen or hiding/unhiding userland threads
|
||||
the help screen or hiding/unhiding userland threads.
|
||||
If a thread is currently selected the selection is updated to point
|
||||
to the thread's parent process. (thanks to Gonzalo, et.al.)
|
||||
* Reorder process scanning to be performed before updating the display
|
||||
of the meters in the header
|
||||
* Always check the user for a process for any changes
|
||||
* Always check the user for a process for any changes.
|
||||
This affects multiple platforms that previously didn't correctly handle
|
||||
the user field for a process to change at runtime (e.g. due to seteuid
|
||||
or similar syscalls).
|
||||
@ -105,14 +105,14 @@ What's new in version 3.1.0
|
||||
* Correctly color the ZFS ARC ratio (thanks to Ross Williams)
|
||||
* Bugfixes related to CPU time display/calculations for darwin on M1 systems
|
||||
(thanks to Alexander Momchilov)
|
||||
* Harmonize the handling of multiple batteries across different platforms
|
||||
* Harmonize the handling of multiple batteries across different platforms.
|
||||
The system is now considered to run on AC if at least one power supply
|
||||
marked as AC is found in the system.
|
||||
Battery capacity is summed up over all batteries found.
|
||||
This also changes the old behavior that batteries reported by the
|
||||
system after the first AC adapter where sometimes ignored.
|
||||
* Correctly handle multiple batteries on Darwin
|
||||
Resolves a possible memory leak on systems with multiple batteries
|
||||
* Correctly handle multiple batteries on Darwin.
|
||||
Resolves a possible memory leak on systems with multiple batteries.
|
||||
* Handle Linux Shmem being part of Cached in the MemoryMeter
|
||||
* Add SwapCached to the Linux swap meter (thanks to David Zarzycki)
|
||||
* Convert process time to days if applicable (thanks to David Zarzycki)
|
||||
@ -138,7 +138,7 @@ What's new in version 3.1.0
|
||||
* Add command line option to drop Linux capabilities
|
||||
* Support scheduler affinity on platforms beyond Linux
|
||||
* Report on any failure to write the configuration file
|
||||
* Cache stderr to be able to print assert messages
|
||||
* Cache stderr to be able to print assert messages.
|
||||
These messages are shown in case htop terminates unexpectedly.
|
||||
* Print current settings on crash
|
||||
* Reset signal handlers on program exit
|
||||
|
@ -118,12 +118,6 @@ const PanelClass ColumnsPanel_class = {
|
||||
.eventHandler = ColumnsPanel_eventHandler
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
Panel* super;
|
||||
unsigned int id;
|
||||
unsigned int offset;
|
||||
} DynamicIterator;
|
||||
|
||||
static void ColumnsPanel_add(Panel* super, unsigned int key, Hashtable* columns) {
|
||||
const char* name;
|
||||
if (key < LAST_PROCESSFIELD) {
|
||||
|
@ -393,10 +393,8 @@ int CommandLine_run(const char* name, int argc, char** argv) {
|
||||
|
||||
/* Delete these last, since they can get accessed in the crash handler */
|
||||
Settings_delete(settings);
|
||||
if (dc)
|
||||
Hashtable_delete(dc);
|
||||
if (dm)
|
||||
Hashtable_delete(dm);
|
||||
DynamicColumns_delete(dc);
|
||||
DynamicMeters_delete(dm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -22,6 +22,13 @@ Hashtable* DynamicColumns_new(void) {
|
||||
return Platform_dynamicColumns();
|
||||
}
|
||||
|
||||
void DynamicColumns_delete(Hashtable* dynamics) {
|
||||
if (dynamics) {
|
||||
Platform_dynamicColumnsDone(dynamics);
|
||||
Hashtable_delete(dynamics);
|
||||
}
|
||||
}
|
||||
|
||||
const char* DynamicColumn_init(unsigned int key) {
|
||||
return Platform_dynamicColumnInit(key);
|
||||
}
|
||||
|
@ -21,6 +21,8 @@ typedef struct DynamicColumn_ {
|
||||
|
||||
Hashtable* DynamicColumns_new(void);
|
||||
|
||||
void DynamicColumns_delete(Hashtable* dynamics);
|
||||
|
||||
const char* DynamicColumn_init(unsigned int key);
|
||||
|
||||
const DynamicColumn* DynamicColumn_lookup(Hashtable* dynamics, unsigned int key);
|
||||
|
@ -38,6 +38,13 @@ Hashtable* DynamicMeters_new(void) {
|
||||
return Platform_dynamicMeters();
|
||||
}
|
||||
|
||||
void DynamicMeters_delete(Hashtable* dynamics) {
|
||||
if (dynamics) {
|
||||
Platform_dynamicMetersDone(dynamics);
|
||||
Hashtable_delete(dynamics);
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
unsigned int key;
|
||||
const char* name;
|
||||
|
@ -17,6 +17,8 @@ typedef struct DynamicMeter_ {
|
||||
|
||||
Hashtable* DynamicMeters_new(void);
|
||||
|
||||
void DynamicMeters_delete(Hashtable* dynamics);
|
||||
|
||||
const char* DynamicMeter_lookup(Hashtable* dynamics, unsigned int key);
|
||||
|
||||
bool DynamicMeter_search(Hashtable* dynamics, const char* name, unsigned int* key);
|
||||
|
20
Header.c
20
Header.c
@ -86,8 +86,9 @@ static void Header_addMeterByName(Header* this, const char* name, MeterModeId mo
|
||||
if (paren) {
|
||||
int ok = sscanf(paren, "(%10u)", ¶m); // CPUMeter
|
||||
if (!ok) {
|
||||
char* end, dynamic[32] = {0};
|
||||
char dynamic[32] = {0};
|
||||
if (sscanf(paren, "(%30s)", dynamic)) { // DynamicMeter
|
||||
char* end;
|
||||
if ((end = strrchr(dynamic, ')')) == NULL)
|
||||
return; // htoprc parse failure
|
||||
*end = '\0';
|
||||
@ -195,25 +196,28 @@ void Header_draw(const Header* this) {
|
||||
}
|
||||
const int width = COLS - pad;
|
||||
int x = pad;
|
||||
float roundingLoss = 0.0f;
|
||||
float roundingLoss = 0.0F;
|
||||
|
||||
Header_forEachColumn(this, col) {
|
||||
Vector* meters = this->columns[col];
|
||||
float colWidth = (float)width * HeaderLayout_layouts[this->headerLayout].widths[col] / 100.0f;
|
||||
float colWidth = (float)width * HeaderLayout_layouts[this->headerLayout].widths[col] / 100.0F;
|
||||
|
||||
roundingLoss += colWidth - floorf(colWidth);
|
||||
if (roundingLoss >= 1.0f) {
|
||||
colWidth += 1.0f;
|
||||
roundingLoss -= 1.0f;
|
||||
if (roundingLoss >= 1.0F) {
|
||||
colWidth += 1.0F;
|
||||
roundingLoss -= 1.0F;
|
||||
}
|
||||
|
||||
for (int y = (pad / 2), i = 0; i < Vector_size(meters); i++) {
|
||||
Meter* meter = (Meter*) Vector_get(meters, i);
|
||||
|
||||
float actualWidth = colWidth;
|
||||
if (meter->mode == TEXT_METERMODE) {
|
||||
|
||||
/* Let meters in text mode expand to the right on empty neighbors;
|
||||
except for multi column meters. */
|
||||
if (meter->mode == TEXT_METERMODE && !Meter_isMultiColumn(meter)) {
|
||||
for (int j = 1; j < meter->columnWidthCount; j++) {
|
||||
actualWidth += (float)width * HeaderLayout_layouts[this->headerLayout].widths[col + j] / 100.0f;
|
||||
actualWidth += (float)width * HeaderLayout_layouts[this->headerLayout].widths[col + j] / 100.0F;
|
||||
}
|
||||
}
|
||||
|
||||
|
2
Header.h
2
Header.h
@ -23,7 +23,7 @@ typedef struct Header_ {
|
||||
int height;
|
||||
} Header;
|
||||
|
||||
#define Header_forEachColumn(this_, i_) for (size_t (i_)=0; (i_) < HeaderLayout_getColumns((this_)->headerLayout); ++(i_))
|
||||
#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);
|
||||
|
||||
|
@ -82,6 +82,6 @@ HeaderOptionsPanel* HeaderOptionsPanel_new(Settings* settings, ScreenManager* sc
|
||||
for (int i = 0; i < LAST_HEADER_LAYOUT; i++) {
|
||||
Panel_add(super, (Object*) CheckItem_newByVal(HeaderLayout_layouts[i].description, false));
|
||||
}
|
||||
CheckItem_set((CheckItem*)Panel_get(super, settings->hLayout), true);
|
||||
CheckItem_set((CheckItem*)Panel_get(super, scr->header->headerLayout), true);
|
||||
return this;
|
||||
}
|
||||
|
@ -61,8 +61,8 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
|
||||
if (ch == KEY_RESIZE)
|
||||
return IGNORED;
|
||||
|
||||
/* reset on every normal key */
|
||||
if (ch != ERR)
|
||||
/* reset on every normal key, except mouse events while mouse support is disabled */
|
||||
if (ch != ERR && (ch != KEY_MOUSE || this->state->settings->enableMouse))
|
||||
this->state->hideProcessSelection = false;
|
||||
|
||||
if (EVENT_IS_HEADER_CLICK(ch)) {
|
||||
|
@ -33,10 +33,14 @@ static void MemorySwapMeter_updateValues(Meter* this) {
|
||||
static void MemorySwapMeter_draw(Meter* this, int x, int y, int w) {
|
||||
MemorySwapMeterData* data = this->meterData;
|
||||
|
||||
/* Use the same width for each sub meter to align with CPU meter */
|
||||
const int colwidth = w / 2;
|
||||
const int diff = w - colwidth * 2;
|
||||
|
||||
assert(data->memoryMeter->draw);
|
||||
data->memoryMeter->draw(data->memoryMeter, x, y, w / 2);
|
||||
data->memoryMeter->draw(data->memoryMeter, x, y, colwidth);
|
||||
assert(data->swapMeter->draw);
|
||||
data->swapMeter->draw(data->swapMeter, x + w / 2, y, w - w / 2);
|
||||
data->swapMeter->draw(data->swapMeter, x + colwidth + diff, y, colwidth);
|
||||
}
|
||||
|
||||
static void MemorySwapMeter_init(Meter* this) {
|
||||
@ -91,6 +95,7 @@ const MeterClass MemorySwapMeter_class = {
|
||||
},
|
||||
.updateValues = MemorySwapMeter_updateValues,
|
||||
.defaultMode = CUSTOM_METERMODE,
|
||||
.isMultiColumn = true,
|
||||
.name = "MemorySwap",
|
||||
.uiName = "Memory & Swap",
|
||||
.description = "Combined memory and swap usage",
|
||||
|
2
Meter.h
2
Meter.h
@ -73,6 +73,7 @@ typedef struct MeterClass_ {
|
||||
const char* const caption; /* prefix in the actual header */
|
||||
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) */
|
||||
} MeterClass;
|
||||
|
||||
#define As_Meter(this_) ((const MeterClass*)((this_)->super.klass))
|
||||
@ -92,6 +93,7 @@ typedef struct MeterClass_ {
|
||||
#define Meter_attributes(this_) As_Meter(this_)->attributes
|
||||
#define Meter_name(this_) As_Meter(this_)->name
|
||||
#define Meter_uiName(this_) As_Meter(this_)->uiName
|
||||
#define Meter_isMultiColumn(this_) As_Meter(this_)->isMultiColumn
|
||||
|
||||
typedef struct GraphData_ {
|
||||
struct timeval time;
|
||||
|
55
Process.c
55
Process.c
@ -707,6 +707,26 @@ void Process_printLeftAlignedField(RichString* str, int attr, const char* conten
|
||||
RichString_appendChr(str, attr, ' ', width + 1 - columns);
|
||||
}
|
||||
|
||||
void Process_printPercentage(float val, char* buffer, int n, int* attr) {
|
||||
if (val >= 0) {
|
||||
if (val < 99.9F) {
|
||||
if (val < 0.05F) {
|
||||
*attr = CRT_colors[PROCESS_SHADOW];
|
||||
}
|
||||
xSnprintf(buffer, n, "%4.1f ", val);
|
||||
} else if (val < 999) {
|
||||
*attr = CRT_colors[PROCESS_MEGABYTES];
|
||||
xSnprintf(buffer, n, "%3d. ", (int)val);
|
||||
} else {
|
||||
*attr = CRT_colors[PROCESS_MEGABYTES];
|
||||
xSnprintf(buffer, n, "%4d ", (int)val);
|
||||
}
|
||||
} else {
|
||||
*attr = CRT_colors[PROCESS_SHADOW];
|
||||
xSnprintf(buffer, n, " N/A ");
|
||||
}
|
||||
}
|
||||
|
||||
void Process_writeField(const Process* this, RichString* str, ProcessField field) {
|
||||
char buffer[256];
|
||||
size_t n = sizeof(buffer);
|
||||
@ -821,34 +841,13 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field
|
||||
|
||||
xSnprintf(buffer, n, "%4ld ", this->nlwp);
|
||||
break;
|
||||
case PERCENT_CPU:
|
||||
case PERCENT_CPU: Process_printPercentage(this->percent_cpu, buffer, n, &attr); break;
|
||||
case PERCENT_NORM_CPU: {
|
||||
float cpuPercentage = this->percent_cpu;
|
||||
if (field == PERCENT_NORM_CPU) {
|
||||
cpuPercentage /= this->processList->activeCPUs;
|
||||
}
|
||||
if (cpuPercentage > 999.9F) {
|
||||
xSnprintf(buffer, n, "%4u ", (unsigned int)cpuPercentage);
|
||||
} else if (cpuPercentage > 99.9F) {
|
||||
xSnprintf(buffer, n, "%3u. ", (unsigned int)cpuPercentage);
|
||||
} else {
|
||||
if (cpuPercentage < 0.05F)
|
||||
attr = CRT_colors[PROCESS_SHADOW];
|
||||
|
||||
xSnprintf(buffer, n, "%4.1f ", cpuPercentage);
|
||||
}
|
||||
float cpuPercentage = this->percent_cpu / this->processList->activeCPUs;
|
||||
Process_printPercentage(cpuPercentage, buffer, n, &attr);
|
||||
break;
|
||||
}
|
||||
case PERCENT_MEM:
|
||||
if (this->percent_mem > 99.9F) {
|
||||
xSnprintf(buffer, n, "100. ");
|
||||
} else {
|
||||
if (this->percent_mem < 0.05F)
|
||||
attr = CRT_colors[PROCESS_SHADOW];
|
||||
|
||||
xSnprintf(buffer, n, "%4.1f ", this->percent_mem);
|
||||
}
|
||||
break;
|
||||
case PERCENT_MEM: Process_printPercentage(this->percent_mem, buffer, n, &attr); break;
|
||||
case PGRP: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->pgrp); break;
|
||||
case PID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->pid); break;
|
||||
case PPID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->ppid); break;
|
||||
@ -864,8 +863,12 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field
|
||||
case STATE:
|
||||
xSnprintf(buffer, n, "%c ", this->state);
|
||||
switch (this->state) {
|
||||
#ifdef HTOP_NETBSD
|
||||
case 'P':
|
||||
#else
|
||||
case 'R':
|
||||
attr = CRT_colors[PROCESS_R_STATE];
|
||||
#endif
|
||||
attr = CRT_colors[PROCESS_RUN_STATE];
|
||||
break;
|
||||
case 'D':
|
||||
attr = CRT_colors[PROCESS_D_STATE];
|
||||
|
@ -79,8 +79,6 @@ typedef struct ProcessMergedCommand_ {
|
||||
char* str; /* merged Command string */
|
||||
size_t highlightCount; /* how many portions of cmdline to highlight */
|
||||
ProcessCmdlineHighlight highlights[8]; /* which portions of cmdline to highlight */
|
||||
bool separateComm : 1; /* whether comm is a separate field */
|
||||
bool unmatchedExe : 1; /* whether exe matched with cmdline */
|
||||
bool cmdlineChanged : 1; /* whether cmdline changed */
|
||||
bool exeChanged : 1; /* whether exe changed */
|
||||
bool commChanged : 1; /* whether comm changed */
|
||||
@ -358,6 +356,8 @@ void Process_fillStarttimeBuffer(Process* this);
|
||||
|
||||
void Process_printLeftAlignedField(RichString* str, int attr, const char* content, unsigned int width);
|
||||
|
||||
void Process_printPercentage(float val, char* buffer, int n, int* attr);
|
||||
|
||||
void Process_display(const Object* cast, RichString* out);
|
||||
|
||||
void Process_done(Process* this);
|
||||
|
98
README
98
README
@ -1,10 +1,12 @@
|
||||
# [](https://htop.dev)
|
||||
# [](https://htop.dev)
|
||||
|
||||
[](https://github.com/htop-dev/htop/actions)
|
||||
[](https://scan.coverity.com/projects/21665)
|
||||
[](https://groups.io/g/htop)
|
||||
[](https://web.libera.chat/#htop)
|
||||
[](https://github.com/htop-dev/htop/releases/latest)
|
||||
[](https://github.com/htop-dev/htop/releases/latest)
|
||||
[](https://repology.org/project/htop/versions)
|
||||
[](COPYING?raw=true)
|
||||
|
||||

|
||||
|
||||
@ -13,22 +15,26 @@
|
||||
`htop` is a cross-platform interactive process viewer.
|
||||
|
||||
`htop` allows scrolling the list of processes vertically and horizontally to see their full command lines and related information like memory and CPU consumption.
|
||||
Also system wide information, like load average or swap usage, is shown.
|
||||
|
||||
The information displayed is configurable through a graphical setup and can be sorted and filtered interactively.
|
||||
|
||||
Tasks related to processes (e.g. killing and renicing) can be done without entering their PIDs.
|
||||
|
||||
Running `htop` requires `ncurses` libraries (typically named libncursesw*).
|
||||
Running `htop` requires `ncurses` libraries, typically named libncurses(w).
|
||||
|
||||
For more information and details on how to contribute to `htop` visit [htop.dev](https://htop.dev).
|
||||
`htop` is written in C.
|
||||
|
||||
For more information and details visit [htop.dev](https://htop.dev).
|
||||
|
||||
## Build instructions
|
||||
|
||||
### Prerequisite
|
||||
List of build-time dependencies:
|
||||
* `build-essential` standard GNU autotools-based
|
||||
* `autoconf`
|
||||
* `autotools`
|
||||
* standard GNU autotools-based C toolchain
|
||||
- C99 compliant compiler
|
||||
- `autoconf`
|
||||
- `autotools`
|
||||
* `ncurses`
|
||||
|
||||
**Note about `ncurses`:**
|
||||
@ -42,27 +48,26 @@ List of additional build-time dependencies (based on feature flags):
|
||||
* `sensors`
|
||||
* `hwloc`
|
||||
* `libcap`
|
||||
* `libnl-3`
|
||||
|
||||
Compiling `htop` requires the header files for `ncurses` . Install these and other required packages for C development from your package manager.
|
||||
Install these and other required packages for C development from your package manager.
|
||||
|
||||
**Debian/Ubuntu**
|
||||
~~~ shell
|
||||
sudo apt install libncursesw5-dev autotools-dev autoconf
|
||||
sudo apt install libncursesw5-dev autotools-dev autoconf build-essential
|
||||
~~~
|
||||
|
||||
**Fedora/RHEL**
|
||||
~~~ shell
|
||||
sudo dnf install ncurses-devel automake autoconf
|
||||
sudo dnf install ncurses-devel automake autoconf gcc
|
||||
~~~
|
||||
|
||||
### Compiling from source:
|
||||
To compile from sources downloaded from the Git repository (`git clone` or downloads from [Github releases](https://github.com/htop-dev/htop/releases/)), then run:
|
||||
### Compile from source:
|
||||
To compile from source, download from the Git repository (`git clone` or downloads from [GitHub releases](https://github.com/htop-dev/htop/releases/)), then run:
|
||||
~~~ shell
|
||||
./autogen.sh && ./configure && make
|
||||
~~~
|
||||
|
||||
By default `make install` will install into `/usr/local`, for changing the path use `./configure --prefix=/some/path`.
|
||||
|
||||
### Install
|
||||
To install on the local system run `make install`. By default `make install` installs into `/usr/local`. To change this path use `./configure --prefix=/some/path`.
|
||||
|
||||
@ -74,52 +79,55 @@ To install on the local system run `make install`. By default `make install` ins
|
||||
|
||||
* `--enable-unicode`:
|
||||
enable Unicode support
|
||||
dependency: *libncursesw*
|
||||
default: *yes*
|
||||
* `--enable-pcp`:
|
||||
enable Performance Co-Pilot support via a new pcp-htop utility
|
||||
dependency: *libpcp*
|
||||
default: *no*
|
||||
- dependency: *libncursesw*
|
||||
- default: *yes*
|
||||
* `--enable-affinity`:
|
||||
enable `sched_setaffinity(2)` and `sched_getaffinity(2)` for affinity support; conflicts with hwloc
|
||||
default: *check*
|
||||
- default: *check*
|
||||
* `--enable-hwloc`:
|
||||
enable hwloc support for CPU affinity; disables affinity support
|
||||
dependency: *libhwloc*
|
||||
default: *no*
|
||||
- dependency: *libhwloc*
|
||||
- default: *no*
|
||||
* `--enable-static`:
|
||||
build a static htop binary; hwloc and delay accounting are not supported
|
||||
default: *no*
|
||||
- default: *no*
|
||||
* `--enable-debug`:
|
||||
Enable asserts and internal sanity checks; implies a performance penalty
|
||||
default: *no*
|
||||
- default: *no*
|
||||
|
||||
#### Performance Co-Pilot
|
||||
|
||||
* `--enable-pcp`:
|
||||
enable Performance Co-Pilot support via a new pcp-htop utility
|
||||
- dependency: *libpcp*
|
||||
- default: *no*
|
||||
|
||||
#### Linux
|
||||
|
||||
* `--enable-sensors`:
|
||||
enable libsensors(3) support for reading temperature data
|
||||
dependencies: *libsensors-dev*(build-time), at runtime *libsensors* is loaded via `dlopen(3)` if available
|
||||
default: *check*
|
||||
- dependencies: *libsensors-dev*(build-time), at runtime *libsensors* is loaded via `dlopen(3)` if available
|
||||
- default: *check*
|
||||
* `--enable-capabilities`:
|
||||
enable Linux capabilities support
|
||||
dependency: *libcap*
|
||||
default: *check*
|
||||
- dependency: *libcap*
|
||||
- default: *check*
|
||||
* `--with-proc`:
|
||||
location of a Linux-compatible proc filesystem
|
||||
default: */proc*
|
||||
- default: */proc*
|
||||
* `--enable-openvz`:
|
||||
enable OpenVZ support
|
||||
default: *no*
|
||||
- default: *no*
|
||||
* `--enable-vserver`:
|
||||
enable VServer support
|
||||
default: *no*
|
||||
- default: *no*
|
||||
* `--enable-ancient-vserver`:
|
||||
enable ancient VServer support (implies `--enable-vserver`)
|
||||
default: *no*
|
||||
- default: *no*
|
||||
* `--enable-delayacct`:
|
||||
enable Linux delay accounting support
|
||||
dependencies: *pkg-config*(build-time), *libnl-3* and *libnl-genl-3*
|
||||
default: *check*
|
||||
- dependencies: *pkg-config*(build-time), *libnl-3* and *libnl-genl-3*
|
||||
- default: *check*
|
||||
|
||||
|
||||
## Runtime dependencies:
|
||||
@ -128,20 +136,22 @@ To install on the local system run `make install`. By default `make install` ins
|
||||
|
||||
### Runtime optional dependencies:
|
||||
`htop` has a set of fixed optional dependencies, depending on build/configure option used:
|
||||
* `libdl`, if not building static and support for some of the optional libraries is enabled, is always required when support for to optionally load dependencies (i.e. `libsensors`, `systemd`) is present.
|
||||
|
||||
#### Linux
|
||||
* `libdl`, if not building a static binary, is always required when support for to optionally dependencies (i.e. `libsensors`, `libsystemd`) is present.
|
||||
* `libcap`, user-space interfaces to the POSIX 1003.1e, is always required when `--enable-capabilities` was used to configure `htop`.
|
||||
* `libsensors`, readout of temperatures and CPU speeds, is optional even when `--enable-sensors` was used to configure `htop`.
|
||||
* `systemd` is optional when `--enable-static` was not used to configure `htop` (Linux only). If building statically and `libsystemd` is not found by `configure` support for the SystemD meter is disabled entirely.
|
||||
* `libsystemd` is optional when `--enable-static` was not used to configure `htop`. If building statically and `libsystemd` is not found by `configure`, support for the systemd meter is disabled entirely.
|
||||
|
||||
`htop` checks for the availability of the actual runtime lib as `htop` runs.
|
||||
|
||||
**BSD**
|
||||
On most *BSD systems you also have `kvm` as a static requirement to read all the kernel information.
|
||||
#### BSD
|
||||
On most BSD systems `kvm` is a requirement to read kernel information.
|
||||
|
||||
More information on required and optional dependencies can be found in [configure.ac](configure.ac).
|
||||
|
||||
## Usage
|
||||
See the manual page (`man htop`) or the on-line help ('F1' or 'h' inside `htop`) for a list of supported key commands.
|
||||
See the manual page (`man htop`) or the help menu (**F1** or **h** inside `htop`) for a list of supported key commands.
|
||||
|
||||
## Support
|
||||
|
||||
@ -151,14 +161,14 @@ If you have trouble running `htop` please consult your Operating System / Linux
|
||||
|
||||
We have a [development mailing list](https://htop.dev/mailinglist.html). Feel free to subscribe for release announcements or asking questions on the development of htop.
|
||||
|
||||
You can also join our IRC channel #htop on Libera.Chat and talk to the developers there.
|
||||
You can also join our IRC channel [#htop on Libera.Chat](https://web.libera.chat/#htop) and talk to the developers there.
|
||||
|
||||
If you have found an issue with the source of htop, please check whether this has already been reported in our [Github issue tracker](https://github.com/htop-dev/htop/issues).
|
||||
If not, please file a new issue describing the problem you have found, the location in the source code you are referring to and a possible fix.
|
||||
If you have found an issue within the source of htop, please check whether this has already been reported in our [GitHub issue tracker](https://github.com/htop-dev/htop/issues).
|
||||
If not, please file a new issue describing the problem you have found, the potential location in the source code you are referring to and a possible fix if available.
|
||||
|
||||
## History
|
||||
|
||||
`htop` was invented, developed and maintained by Hisham Muhammad from 2004 to 2019. His [legacy repository](https://github.com/hishamhm/htop/) has been archived to preserve the history.
|
||||
`htop` was invented, developed and maintained by [Hisham Muhammad](https://hisham.hm/) from 2004 to 2019. His [legacy repository](https://github.com/hishamhm/htop/) has been archived to preserve the history.
|
||||
|
||||
In 2020 a [team](https://github.com/orgs/htop-dev/people) took over the development amicably and continues to maintain `htop` collaboratively.
|
||||
|
||||
|
16
Settings.c
16
Settings.c
@ -134,8 +134,9 @@ static void Settings_readFields(Settings* settings, const char* line) {
|
||||
}
|
||||
|
||||
// Dynamically-defined columns are always stored by-name.
|
||||
char* end, dynamic[32] = {0};
|
||||
char dynamic[32] = {0};
|
||||
if (sscanf(ids[i], "Dynamic(%30s)", dynamic)) {
|
||||
char* end;
|
||||
if ((end = strrchr(dynamic, ')')) == NULL)
|
||||
continue;
|
||||
*end = '\0';
|
||||
@ -180,8 +181,10 @@ static bool Settings_read(Settings* this, const char* fileName, unsigned int ini
|
||||
this->config_version = atoi(option[1]);
|
||||
if (this->config_version > CONFIG_READER_MIN_VERSION) {
|
||||
// the version of the config file on disk is newer than what we can read
|
||||
fprintf(stderr, "WARNING: %s specifies configuration format version v%d, but this %s binary supports up to v%d.", fileName, this->config_version, PACKAGE, CONFIG_READER_MIN_VERSION);
|
||||
fprintf(stderr, "WARNING: %s specifies configuration format version v%d, but this %s binary supports up to v%d\n.", fileName, this->config_version, PACKAGE, CONFIG_READER_MIN_VERSION);
|
||||
fprintf(stderr, " The configuration version will be downgraded to v%d when %s exits.\n", CONFIG_READER_MIN_VERSION, PACKAGE);
|
||||
String_freeArray(option);
|
||||
fclose(fd);
|
||||
return false;
|
||||
}
|
||||
} else if (String_eq(option[0], "fields")) {
|
||||
@ -355,9 +358,9 @@ int Settings_write(const Settings* this, bool onCrash) {
|
||||
}
|
||||
|
||||
#define printSettingInteger(setting_, value_) \
|
||||
fprintf(fd, setting_ "=%d%c", (int) value_, separator);
|
||||
fprintf(fd, setting_ "=%d%c", (int) (value_), separator)
|
||||
#define printSettingString(setting_, value_) \
|
||||
fprintf(fd, setting_ "=%s%c", value_, separator);
|
||||
fprintf(fd, setting_ "=%s%c", value_, separator)
|
||||
|
||||
if (!onCrash) {
|
||||
fprintf(fd, "# Beware! This file is rewritten by htop when settings are changed in the interface.\n");
|
||||
@ -539,7 +542,10 @@ Settings* Settings_new(unsigned int initialCpuCount, Hashtable* dynamicColumns)
|
||||
}
|
||||
if (!ok) {
|
||||
this->changed = true;
|
||||
Settings_read(this, SYSCONFDIR "/htoprc", initialCpuCount);
|
||||
ok = Settings_read(this, SYSCONFDIR "/htoprc", initialCpuCount);
|
||||
}
|
||||
if (!ok) {
|
||||
Settings_defaultMeters(this, initialCpuCount);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
16
XUtils.c
16
XUtils.c
@ -78,6 +78,22 @@ void* xReallocArray(void* ptr, size_t nmemb, size_t size) {
|
||||
return xRealloc(ptr, nmemb * size);
|
||||
}
|
||||
|
||||
void* xReallocArrayZero(void* ptr, size_t prevmemb, size_t newmemb, size_t size) {
|
||||
assert((ptr == NULL) == (prevmemb == 0));
|
||||
|
||||
if (prevmemb == newmemb) {
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void* ret = xReallocArray(ptr, newmemb, size);
|
||||
|
||||
if (newmemb > prevmemb) {
|
||||
memset((unsigned char*)ret + prevmemb * size, '\0', (newmemb - prevmemb) * size);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline bool String_contains_i(const char* s1, const char* s2) {
|
||||
return strcasestr(s1, s2) != NULL;
|
||||
}
|
||||
|
2
XUtils.h
2
XUtils.h
@ -30,6 +30,8 @@ void* xRealloc(void* ptr, size_t size) ATTR_ALLOC_SIZE1(2);
|
||||
|
||||
void* xReallocArray(void* ptr, size_t nmemb, size_t size) ATTR_ALLOC_SIZE2(2, 3);
|
||||
|
||||
void* xReallocArrayZero(void* ptr, size_t prevmemb, size_t newmemb, size_t size) ATTR_ALLOC_SIZE2(3, 4);
|
||||
|
||||
/*
|
||||
* String_startsWith gives better performance if strlen(match) can be computed
|
||||
* at compile time (e.g. when they are immutable string literals). :)
|
||||
|
@ -6,7 +6,7 @@
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
AC_PREREQ([2.69])
|
||||
AC_INIT([htop], [3.1.0-dev], [htop@groups.io], [], [https://htop.dev/])
|
||||
AC_INIT([htop], [3.1.0-rc2], [htop@groups.io], [], [https://htop.dev/])
|
||||
|
||||
AC_CONFIG_SRCDIR([htop.c])
|
||||
AC_CONFIG_AUX_DIR([build-aux])
|
||||
@ -367,7 +367,7 @@ if test "$enable_static" = yes; then
|
||||
fi
|
||||
if test "$my_htop_platform" = "solaris"; then
|
||||
# On OmniOS /usr/include/sys/regset.h redefines ERR to 13 - \r, breaking the Enter key.
|
||||
# Since ncruses macros use the ERR macro, we can not use another name.
|
||||
# Since ncurses macros use the ERR macro, we can not use another name.
|
||||
AC_DEFINE([ERR], [(-1)], [Predefine ncurses macro.])
|
||||
fi
|
||||
AC_CHECK_FUNCS( [set_escdelay] )
|
||||
@ -482,6 +482,9 @@ AC_ARG_ENABLE([ancient_vserver],
|
||||
[],
|
||||
[enable_ancient_vserver=no])
|
||||
if test "x$enable_ancient_vserver" = xyes; then
|
||||
if test "x$enable_vserver" != xyes; then
|
||||
enable_vserver=implied
|
||||
fi
|
||||
AC_DEFINE([HAVE_VSERVER], [1], [Define if VServer support enabled.])
|
||||
AC_DEFINE([HAVE_ANCIENT_VSERVER], [1], [Define if ancient vserver support enabled.])
|
||||
fi
|
||||
|
@ -269,7 +269,7 @@ ERROR_A:
|
||||
Process_updateCmdline(proc, k->kp_proc.p_comm, 0, strlen(k->kp_proc.p_comm));
|
||||
}
|
||||
|
||||
// Converts nanoseconds to hundreths of a second (centiseconds) as needed by the "time" field of the Process struct.
|
||||
// Converts nanoseconds to hundredths of a second (centiseconds) as needed by the "time" field of the Process struct.
|
||||
static long long int nanosecondsToCentiseconds(uint64_t nanoseconds) {
|
||||
const uint64_t centiseconds_per_second = 100;
|
||||
const uint64_t nanoseconds_per_second = 1e9;
|
||||
|
@ -432,7 +432,8 @@ void Platform_gettime_monotonic(uint64_t* msec) {
|
||||
|
||||
#else
|
||||
|
||||
Generic_gettime_monotomic(msec);
|
||||
Generic_gettime_monotonic(msec);
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
@ -99,6 +99,8 @@ void Platform_gettime_monotonic(uint64_t* msec);
|
||||
|
||||
static inline Hashtable* Platform_dynamicMeters(void) { return NULL; }
|
||||
|
||||
static inline void Platform_dynamicMetersDone(ATTR_UNUSED Hashtable* table) { }
|
||||
|
||||
static inline void Platform_dynamicMeterInit(ATTR_UNUSED Meter* meter) { }
|
||||
|
||||
static inline void Platform_dynamicMeterUpdateValues(ATTR_UNUSED Meter* meter) { }
|
||||
@ -107,6 +109,8 @@ static inline void Platform_dynamicMeterDisplay(ATTR_UNUSED const Meter* meter,
|
||||
|
||||
static inline Hashtable* Platform_dynamicColumns(void) { return NULL; }
|
||||
|
||||
static inline void Platform_dynamicColumnsDone(ATTR_UNUSED Hashtable* table) { }
|
||||
|
||||
static inline const char* Platform_dynamicColumnInit(ATTR_UNUSED unsigned int key) { return NULL; }
|
||||
|
||||
static inline bool Platform_dynamicColumnWriteField(ATTR_UNUSED const Process* proc, ATTR_UNUSED RichString* str, ATTR_UNUSED unsigned int key) { return false; }
|
||||
|
@ -92,6 +92,8 @@ static inline void Platform_gettime_monotonic(uint64_t* msec) {
|
||||
|
||||
static inline Hashtable* Platform_dynamicMeters(void) { return NULL; }
|
||||
|
||||
static inline void Platform_dynamicMetersDone(ATTR_UNUSED Hashtable* table) { }
|
||||
|
||||
static inline void Platform_dynamicMeterInit(ATTR_UNUSED Meter* meter) { }
|
||||
|
||||
static inline void Platform_dynamicMeterUpdateValues(ATTR_UNUSED Meter* meter) { }
|
||||
@ -100,6 +102,8 @@ static inline void Platform_dynamicMeterDisplay(ATTR_UNUSED const Meter* meter,
|
||||
|
||||
static inline Hashtable* Platform_dynamicColumns(void) { return NULL; }
|
||||
|
||||
static inline void Platform_dynamicColumnsDone(ATTR_UNUSED Hashtable* table) { }
|
||||
|
||||
static inline const char* Platform_dynamicColumnInit(ATTR_UNUSED unsigned int key) { return NULL; }
|
||||
|
||||
static inline bool Platform_dynamicColumnWriteField(ATTR_UNUSED const Process* proc, ATTR_UNUSED RichString* str, ATTR_UNUSED unsigned int key) { return false; }
|
||||
|
@ -92,6 +92,8 @@ static inline void Platform_gettime_monotonic(uint64_t* msec) {
|
||||
|
||||
static inline Hashtable* Platform_dynamicMeters(void) { return NULL; }
|
||||
|
||||
static inline void Platform_dynamicMetersDone(ATTR_UNUSED Hashtable* table) { }
|
||||
|
||||
static inline void Platform_dynamicMeterInit(ATTR_UNUSED Meter* meter) { }
|
||||
|
||||
static inline void Platform_dynamicMeterUpdateValues(ATTR_UNUSED Meter* meter) { }
|
||||
@ -102,6 +104,8 @@ static inline Hashtable* Platform_dynamicColumns(void) { return NULL; }
|
||||
|
||||
static inline const char* Platform_dynamicColumnInit(ATTR_UNUSED unsigned int key) { return NULL; }
|
||||
|
||||
static inline void Platform_dynamicColumnsDone(ATTR_UNUSED Hashtable* table) { }
|
||||
|
||||
static inline bool Platform_dynamicColumnWriteField(ATTR_UNUSED const Process* proc, ATTR_UNUSED RichString* str, ATTR_UNUSED unsigned int key) { return false; }
|
||||
|
||||
#endif
|
||||
|
@ -392,9 +392,6 @@ except the process's executable instructions).
|
||||
.B M_LRS (LIB)
|
||||
The library size of the process.
|
||||
.TP
|
||||
.B M_DT (DIRTY)
|
||||
The size of the dirty pages of the process.
|
||||
.TP
|
||||
.B M_SWAP (SWAP)
|
||||
The size of the process's swapped pages.
|
||||
.TP
|
||||
|
@ -30,8 +30,6 @@ typedef int IOPriority;
|
||||
|
||||
#define IOPriority_tuple(class_, data_) (((class_) << IOPRIO_CLASS_SHIFT) | (data_))
|
||||
|
||||
#define IOPriority_error 0xffffffff
|
||||
|
||||
#define IOPriority_None IOPriority_tuple(IOPRIO_CLASS_NONE, 0)
|
||||
#define IOPriority_Idle IOPriority_tuple(IOPRIO_CLASS_IDLE, 7)
|
||||
|
||||
|
@ -56,7 +56,6 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
|
||||
[M_TRS] = { .name = "M_TRS", .title = " CODE ", .description = "Size of the text segment of the process", .flags = 0, .defaultSortDesc = true, },
|
||||
[M_DRS] = { .name = "M_DRS", .title = " DATA ", .description = "Size of the data segment plus stack usage of the process", .flags = 0, .defaultSortDesc = true, },
|
||||
[M_LRS] = { .name = "M_LRS", .title = " LIB ", .description = "The library size of the process (calculated from memory maps)", .flags = PROCESS_FLAG_LINUX_LRS_FIX, .defaultSortDesc = true, },
|
||||
[M_DT] = { .name = "M_DT", .title = " DIRTY ", .description = "Size of the dirty pages of the process (unused since Linux 2.6; always 0)", .flags = 0, .defaultSortDesc = true, },
|
||||
[ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .flags = 0, },
|
||||
[PERCENT_CPU] = { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, .defaultSortDesc = true, },
|
||||
[PERCENT_NORM_CPU] = { .name = "PERCENT_NORM_CPU", .title = "NCPU%", .description = "Normalized percentage of the CPU time the process used in the last sampling (normalized by cpu count)", .flags = 0, .defaultSortDesc = true, },
|
||||
@ -88,7 +87,7 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
|
||||
#ifdef HAVE_DELAYACCT
|
||||
[PERCENT_CPU_DELAY] = { .name = "PERCENT_CPU_DELAY", .title = "CPUD%", .description = "CPU delay %", .flags = PROCESS_FLAG_LINUX_DELAYACCT, .defaultSortDesc = true, },
|
||||
[PERCENT_IO_DELAY] = { .name = "PERCENT_IO_DELAY", .title = "IOD% ", .description = "Block I/O delay %", .flags = PROCESS_FLAG_LINUX_DELAYACCT, .defaultSortDesc = true, },
|
||||
[PERCENT_SWAP_DELAY] = { .name = "PERCENT_SWAP_DELAY", .title = "SWAPD% ", .description = "Swapin delay %", .flags = PROCESS_FLAG_LINUX_DELAYACCT, .defaultSortDesc = true, },
|
||||
[PERCENT_SWAP_DELAY] = { .name = "PERCENT_SWAP_DELAY", .title = "SWPD%", .description = "Swapin delay %", .flags = PROCESS_FLAG_LINUX_DELAYACCT, .defaultSortDesc = true, },
|
||||
#endif
|
||||
[M_PSS] = { .name = "M_PSS", .title = " PSS ", .description = "proportional set size, same as M_RESIDENT but each page is divided by the number of processes sharing it", .flags = PROCESS_FLAG_LINUX_SMAPS, .defaultSortDesc = true, },
|
||||
[M_SWAP] = { .name = "M_SWAP", .title = " SWAP ", .description = "Size of the process's swapped pages", .flags = PROCESS_FLAG_LINUX_SMAPS, .defaultSortDesc = true, },
|
||||
@ -190,16 +189,6 @@ bool LinuxProcess_changeAutogroupPriorityBy(Process* this, Arg delta) {
|
||||
return success;
|
||||
}
|
||||
|
||||
#ifdef HAVE_DELAYACCT
|
||||
static void LinuxProcess_printDelay(float delay_percent, char* buffer, int n) {
|
||||
if (isnan(delay_percent)) {
|
||||
xSnprintf(buffer, n, " N/A ");
|
||||
} else {
|
||||
xSnprintf(buffer, n, "%4.1f ", delay_percent);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void LinuxProcess_writeField(const Process* this, RichString* str, ProcessField field) {
|
||||
const LinuxProcess* lp = (const LinuxProcess*) this;
|
||||
bool coloring = this->settings->highlightMegabytes;
|
||||
@ -210,7 +199,6 @@ static void LinuxProcess_writeField(const Process* this, RichString* str, Proces
|
||||
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_DT: Process_printBytes(str, lp->m_dt * pageSize, coloring); return;
|
||||
case M_LRS:
|
||||
if (lp->m_lrs) {
|
||||
Process_printBytes(str, lp->m_lrs * pageSize, coloring);
|
||||
@ -279,9 +267,9 @@ static void LinuxProcess_writeField(const Process* this, RichString* str, Proces
|
||||
break;
|
||||
}
|
||||
#ifdef HAVE_DELAYACCT
|
||||
case PERCENT_CPU_DELAY: LinuxProcess_printDelay(lp->cpu_delay_percent, buffer, n); break;
|
||||
case PERCENT_IO_DELAY: LinuxProcess_printDelay(lp->blkio_delay_percent, buffer, n); break;
|
||||
case PERCENT_SWAP_DELAY: LinuxProcess_printDelay(lp->swapin_delay_percent, buffer, n); break;
|
||||
case PERCENT_CPU_DELAY: Process_printPercentage(lp->cpu_delay_percent, buffer, n, &attr); break;
|
||||
case PERCENT_IO_DELAY: Process_printPercentage(lp->blkio_delay_percent, buffer, n, &attr); break;
|
||||
case PERCENT_SWAP_DELAY: Process_printPercentage(lp->swapin_delay_percent, buffer, n, &attr); break;
|
||||
#endif
|
||||
case CTXT:
|
||||
if (lp->ctxt_diff > 1000) {
|
||||
@ -330,8 +318,6 @@ static int LinuxProcess_compareByKey(const Process* v1, const Process* v2, Proce
|
||||
switch (key) {
|
||||
case M_DRS:
|
||||
return SPACESHIP_NUMBER(p1->m_drs, p2->m_drs);
|
||||
case M_DT:
|
||||
return SPACESHIP_NUMBER(p1->m_dt, p2->m_dt);
|
||||
case M_LRS:
|
||||
return SPACESHIP_NUMBER(p1->m_lrs, p2->m_lrs);
|
||||
case M_TRS:
|
||||
|
@ -47,7 +47,6 @@ typedef struct LinuxProcess_ {
|
||||
long m_trs;
|
||||
long m_drs;
|
||||
long m_lrs;
|
||||
long m_dt;
|
||||
|
||||
/* Data read (in bytes) */
|
||||
unsigned long long io_rchar;
|
||||
|
@ -167,11 +167,11 @@ static void LinuxProcessList_updateCPUcount(ProcessList* super) {
|
||||
|
||||
DIR* dir = opendir("/sys/devices/system/cpu");
|
||||
if (!dir) {
|
||||
super->activeCPUs = 1;
|
||||
super->existingCPUs = 1;
|
||||
this->cpuData = xReallocArray(this->cpuData, 2, sizeof(CPUData));
|
||||
this->cpuData = xReallocArrayZero(this->cpuData, super->existingCPUs ? (super->existingCPUs + 1) : 0, 2, sizeof(CPUData));
|
||||
this->cpuData[0].online = true; /* average is always "online" */
|
||||
this->cpuData[1].online = true;
|
||||
super->activeCPUs = 1;
|
||||
super->existingCPUs = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -204,10 +204,7 @@ static void LinuxProcessList_updateCPUcount(ProcessList* super) {
|
||||
/* readdir() iterates with no specific order */
|
||||
unsigned int max = MAXIMUM(existing, id + 1);
|
||||
if (max > currExisting) {
|
||||
this->cpuData = xReallocArray(this->cpuData, max + /* aggregate */ 1, sizeof(CPUData));
|
||||
for (unsigned int j = currExisting; j < max; j++) {
|
||||
this->cpuData[j].online = false;
|
||||
}
|
||||
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;
|
||||
}
|
||||
@ -678,7 +675,7 @@ static bool LinuxProcessList_readStatmFile(LinuxProcess* process, openat_arg_t p
|
||||
if (!statmfile)
|
||||
return false;
|
||||
|
||||
long int dummy;
|
||||
long int dummy, dummy2;
|
||||
|
||||
int r = fscanf(statmfile, "%ld %ld %ld %ld %ld %ld %ld",
|
||||
&process->super.m_virt,
|
||||
@ -687,7 +684,7 @@ static bool LinuxProcessList_readStatmFile(LinuxProcess* process, openat_arg_t p
|
||||
&process->m_trs,
|
||||
&dummy, /* unused since Linux 2.6; always 0 */
|
||||
&process->m_drs,
|
||||
&process->m_dt);
|
||||
&dummy2); /* unused since Linux 2.6; always 0 */
|
||||
fclose(statmfile);
|
||||
|
||||
if (r == 7) {
|
||||
@ -1426,14 +1423,14 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_
|
||||
{
|
||||
bool prev = proc->usesDeletedLib;
|
||||
|
||||
if ((lp->m_lrs == 0 && (settings->flags & PROCESS_FLAG_LINUX_LRS_FIX)) ||
|
||||
if ((settings->flags & PROCESS_FLAG_LINUX_LRS_FIX) ||
|
||||
(settings->highlightDeletedExe && !proc->procExeDeleted && !proc->isKernelThread && !proc->isUserlandThread)) {
|
||||
// Check if we really should recalculate the M_LRS value for this process
|
||||
uint64_t passedTimeInMs = pl->realtimeMs - lp->last_mlrs_calctime;
|
||||
|
||||
uint64_t recheck = ((uint64_t)rand()) % 2048;
|
||||
|
||||
if (passedTimeInMs > 2000 || passedTimeInMs > recheck) {
|
||||
if (passedTimeInMs > recheck) {
|
||||
lp->last_mlrs_calctime = pl->realtimeMs;
|
||||
LinuxProcessList_readMaps(lp, procFd, settings->flags & PROCESS_FLAG_LINUX_LRS_FIX, settings->highlightDeletedExe);
|
||||
}
|
||||
|
@ -543,6 +543,8 @@ bool Platform_getDiskIO(DiskIOData* data) {
|
||||
if (!fd)
|
||||
return false;
|
||||
|
||||
char lastTopDisk[32] = { '\0' };
|
||||
|
||||
unsigned long long int read_sum = 0, write_sum = 0, timeSpend_sum = 0;
|
||||
char lineBuffer[256];
|
||||
while (fgets(lineBuffer, sizeof(lineBuffer), fd)) {
|
||||
@ -556,22 +558,11 @@ bool Platform_getDiskIO(DiskIOData* data) {
|
||||
continue;
|
||||
|
||||
/* only count root disks, e.g. do not count IO from sda and sda1 twice */
|
||||
if ((diskname[0] == 's' || diskname[0] == 'h')
|
||||
&& diskname[1] == 'd'
|
||||
&& isalpha((unsigned char)diskname[2])
|
||||
&& isdigit((unsigned char)diskname[3]))
|
||||
if (lastTopDisk[0] && String_startsWith(diskname, lastTopDisk))
|
||||
continue;
|
||||
|
||||
/* only count root disks, e.g. do not count IO from mmcblk0 and mmcblk0p1 twice */
|
||||
if (diskname[0] == 'm'
|
||||
&& diskname[1] == 'm'
|
||||
&& diskname[2] == 'c'
|
||||
&& diskname[3] == 'b'
|
||||
&& diskname[4] == 'l'
|
||||
&& diskname[5] == 'k'
|
||||
&& isdigit((unsigned char)diskname[6])
|
||||
&& diskname[7] == 'p')
|
||||
continue;
|
||||
/* This assumes disks are listed directly before any of their partitions */
|
||||
String_safeStrncpy(lastTopDisk, diskname, sizeof(lastTopDisk));
|
||||
|
||||
read_sum += read_tmp;
|
||||
write_sum += write_tmp;
|
||||
|
@ -112,6 +112,8 @@ static inline void Platform_gettime_monotonic(uint64_t* msec) {
|
||||
|
||||
static inline Hashtable* Platform_dynamicMeters(void) { return NULL; }
|
||||
|
||||
static inline void Platform_dynamicMetersDone(ATTR_UNUSED Hashtable* table) { }
|
||||
|
||||
static inline void Platform_dynamicMeterInit(ATTR_UNUSED Meter* meter) { }
|
||||
|
||||
static inline void Platform_dynamicMeterUpdateValues(ATTR_UNUSED Meter* meter) { }
|
||||
@ -120,6 +122,8 @@ static inline void Platform_dynamicMeterDisplay(ATTR_UNUSED const Meter* meter,
|
||||
|
||||
static inline Hashtable* Platform_dynamicColumns(void) { return NULL; }
|
||||
|
||||
static inline void Platform_dynamicColumnsDone(ATTR_UNUSED Hashtable* table) { }
|
||||
|
||||
static inline const char* Platform_dynamicColumnInit(ATTR_UNUSED unsigned int key) { return NULL; }
|
||||
|
||||
static inline bool Platform_dynamicColumnWriteField(ATTR_UNUSED const Process* proc, ATTR_UNUSED RichString* str, ATTR_UNUSED unsigned int key) { return false; }
|
||||
|
@ -19,7 +19,6 @@ in the source distribution for its full text.
|
||||
M_TRS = 42, \
|
||||
M_DRS = 43, \
|
||||
M_LRS = 44, \
|
||||
M_DT = 45, \
|
||||
CTID = 100, \
|
||||
VPID = 101, \
|
||||
VXID = 102, \
|
||||
|
@ -39,14 +39,17 @@ static long fscale;
|
||||
static int pageSize;
|
||||
static int pageSizeKB;
|
||||
|
||||
static char const *freqSysctls[] = {
|
||||
"machdep.est.frequency.current",
|
||||
"machdep.powernow.frequency.current",
|
||||
"machdep.intrepid.frequency.current",
|
||||
"machdep.loongson.frequency.current",
|
||||
"machdep.cpu.frequency.current",
|
||||
"machdep.frequency.current",
|
||||
NULL
|
||||
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) {
|
||||
@ -261,7 +264,6 @@ static void NetBSDProcessList_scanProcs(NetBSDProcessList* this) {
|
||||
bool hideKernelThreads = settings->hideKernelThreads;
|
||||
bool hideUserlandThreads = settings->hideUserlandThreads;
|
||||
int count = 0;
|
||||
int nlwps = 0;
|
||||
|
||||
const struct kinfo_proc2* kprocs = kvm_getproc2(this->kd, KERN_PROC_ALL, 0, sizeof(struct kinfo_proc2), &count);
|
||||
|
||||
@ -279,7 +281,6 @@ static void NetBSDProcessList_scanProcs(NetBSDProcessList* this) {
|
||||
proc->tpgid = kproc->p_tpgid;
|
||||
proc->tgid = kproc->p_pid;
|
||||
proc->session = kproc->p_sid;
|
||||
proc->tty_nr = kproc->p_tdev;
|
||||
proc->pgrp = kproc->p__pgid;
|
||||
proc->isKernelThread = !!(kproc->p_flag & P_SYSTEM);
|
||||
proc->isUserlandThread = proc->pid != proc->tgid;
|
||||
@ -287,6 +288,15 @@ static void NetBSDProcessList_scanProcs(NetBSDProcessList* this) {
|
||||
Process_fillStarttimeBuffer(proc);
|
||||
ProcessList_add(&this->super, proc);
|
||||
|
||||
proc->tty_nr = kproc->p_tdev;
|
||||
const char* name = ((dev_t)kproc->p_tdev != KERN_PROC_TTY_NODEV) ? devname(kproc->p_tdev, S_IFCHR) : NULL;
|
||||
if (!name) {
|
||||
free(proc->tty_name);
|
||||
proc->tty_name = NULL;
|
||||
} else {
|
||||
free_and_xStrdup(&proc->tty_name, name);
|
||||
}
|
||||
|
||||
NetBSDProcessList_updateExe(kproc, proc);
|
||||
NetBSDProcessList_updateProcessName(this->kd, kproc, proc);
|
||||
} else {
|
||||
@ -312,8 +322,12 @@ static void NetBSDProcessList_scanProcs(NetBSDProcessList* this) {
|
||||
proc->nice = kproc->p_nice - 20;
|
||||
proc->time = 100 * (kproc->p_rtime_sec + ((kproc->p_rtime_usec + 500000) / 1000000));
|
||||
proc->priority = kproc->p_priority - PZERO;
|
||||
proc->processor = kproc->p_cpuid;
|
||||
proc->minflt = kproc->p_uru_minflt;
|
||||
proc->majflt = kproc->p_uru_majflt;
|
||||
|
||||
struct kinfo_lwp* klwps = kvm_getlwps(this->kd, kproc->p_pid, kproc->p_paddr, sizeof(struct kinfo_lwp), &nlwps);
|
||||
int nlwps = 0;
|
||||
const struct kinfo_lwp* klwps = kvm_getlwps(this->kd, kproc->p_pid, kproc->p_paddr, sizeof(struct kinfo_lwp), &nlwps);
|
||||
|
||||
switch (kproc->p_realstat) {
|
||||
case SIDL: proc->state = 'I'; break;
|
||||
@ -417,7 +431,7 @@ static void NetBSDProcessList_scanCPUFrequency(NetBSDProcessList* this) {
|
||||
unsigned int cpus = this->super.existingCPUs;
|
||||
bool match = false;
|
||||
char name[64];
|
||||
int freq = 0;
|
||||
long int freq = 0;
|
||||
size_t freqSize;
|
||||
|
||||
for (unsigned int i = 0; i < cpus; i++) {
|
||||
@ -429,7 +443,7 @@ static void NetBSDProcessList_scanCPUFrequency(NetBSDProcessList* this) {
|
||||
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;
|
||||
this->cpuData[i + 1].frequency = freq; /* already in MHz */
|
||||
match = true;
|
||||
}
|
||||
}
|
||||
@ -442,9 +456,10 @@ static void NetBSDProcessList_scanCPUFrequency(NetBSDProcessList* this) {
|
||||
* Iterate through legacy sysctl nodes for single-core frequency until
|
||||
* we find a match...
|
||||
*/
|
||||
for (const char** s = freqSysctls; *s != NULL; ++s) {
|
||||
for (size_t i = 0; i < ARRAYSIZE(freqSysctls); i++) {
|
||||
freqSize = sizeof(freq);
|
||||
if (sysctlbyname(*s, &freq, &freqSize, NULL, 0) != -1) {
|
||||
if (sysctlbyname(freqSysctls[i].name, &freq, &freqSize, NULL, 0) != -1) {
|
||||
freq /= freqSysctls[i].scale; /* scale to MHz */
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
|
@ -260,14 +260,11 @@ double Platform_setCPUValues(Meter* this, 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;
|
||||
this->total = pl->totalMem;
|
||||
this->values[0] = usedMem;
|
||||
this->values[1] = buffersMem;
|
||||
this->values[0] = pl->usedMem;
|
||||
this->values[1] = pl->buffersMem;
|
||||
// this->values[2] = "shared memory, like tmpfs and shm"
|
||||
this->values[3] = cachedMem;
|
||||
this->values[3] = pl->cachedMem;
|
||||
// this->values[4] = "available memory"
|
||||
}
|
||||
|
||||
@ -284,19 +281,19 @@ char* Platform_getProcessEnv(pid_t pid) {
|
||||
char** ptr;
|
||||
int count;
|
||||
kvm_t* kt;
|
||||
struct kinfo_proc* kproc;
|
||||
const struct kinfo_proc2* kproc;
|
||||
size_t capacity = 4096, size = 0;
|
||||
|
||||
if ((kt = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf)) == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((kproc = kvm_getprocs(kt, KERN_PROC_PID, pid, &count)) == NULL) {
|
||||
if ((kproc = kvm_getproc2(kt, KERN_PROC_PID, pid, sizeof(struct kinfo_proc2), &count)) == NULL) {
|
||||
(void) kvm_close(kt);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((ptr = kvm_getenvv(kt, kproc, 0)) == NULL) {
|
||||
if ((ptr = kvm_getenvv2(kt, kproc, 0)) == NULL) {
|
||||
(void) kvm_close(kt);
|
||||
return NULL;
|
||||
}
|
||||
@ -393,7 +390,7 @@ bool Platform_getNetworkIO(NetworkIOData* data) {
|
||||
if (getifaddrs(&ifaddrs) != 0)
|
||||
return false;
|
||||
|
||||
for (struct ifaddrs* ifa = ifaddrs; ifa; ifa = ifa->ifa_next) {
|
||||
for (const struct ifaddrs* ifa = ifaddrs; ifa; ifa = ifa->ifa_next) {
|
||||
if (!ifa->ifa_addr)
|
||||
continue;
|
||||
if (ifa->ifa_addr->sa_family != AF_LINK)
|
||||
@ -401,7 +398,7 @@ bool Platform_getNetworkIO(NetworkIOData* data) {
|
||||
if (ifa->ifa_flags & IFF_LOOPBACK)
|
||||
continue;
|
||||
|
||||
struct if_data* ifd = (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;
|
||||
|
@ -96,6 +96,8 @@ static inline void Platform_gettime_monotonic(uint64_t* msec) {
|
||||
|
||||
static inline Hashtable* Platform_dynamicMeters(void) { return NULL; }
|
||||
|
||||
static inline void Platform_dynamicMetersDone(ATTR_UNUSED Hashtable* table) { }
|
||||
|
||||
static inline void Platform_dynamicMeterInit(ATTR_UNUSED Meter* meter) { }
|
||||
|
||||
static inline void Platform_dynamicMeterUpdateValues(ATTR_UNUSED Meter* meter) { }
|
||||
@ -104,6 +106,8 @@ static inline void Platform_dynamicMeterDisplay(ATTR_UNUSED const Meter* meter,
|
||||
|
||||
static inline Hashtable* Platform_dynamicColumns(void) { return NULL; }
|
||||
|
||||
static inline void Platform_dynamicColumnsDone(ATTR_UNUSED Hashtable* table) { }
|
||||
|
||||
static inline const char* Platform_dynamicColumnInit(ATTR_UNUSED unsigned int key) { return NULL; }
|
||||
|
||||
static inline bool Platform_dynamicColumnWriteField(ATTR_UNUSED const Process* proc, ATTR_UNUSED RichString* str, ATTR_UNUSED unsigned int key) { return false; }
|
||||
|
@ -90,6 +90,8 @@ static inline void Platform_gettime_monotonic(uint64_t* msec) {
|
||||
|
||||
static inline Hashtable* Platform_dynamicMeters(void) { return NULL; }
|
||||
|
||||
static inline void Platform_dynamicMetersDone(ATTR_UNUSED Hashtable* table) { }
|
||||
|
||||
static inline void Platform_dynamicMeterInit(ATTR_UNUSED Meter* meter) { }
|
||||
|
||||
static inline void Platform_dynamicMeterUpdateValues(ATTR_UNUSED Meter* meter) { }
|
||||
@ -98,6 +100,8 @@ static inline void Platform_dynamicMeterDisplay(ATTR_UNUSED const Meter* meter,
|
||||
|
||||
static inline Hashtable* Platform_dynamicColumns(void) { return NULL; }
|
||||
|
||||
static inline void Platform_dynamicColumnsDone(ATTR_UNUSED Hashtable* table) { }
|
||||
|
||||
static inline const char* Platform_dynamicColumnInit(ATTR_UNUSED unsigned int key) { return NULL; }
|
||||
|
||||
static inline bool Platform_dynamicColumnWriteField(ATTR_UNUSED const Process* proc, ATTR_UNUSED RichString* str, ATTR_UNUSED unsigned int key) { return false; }
|
||||
|
@ -226,6 +226,18 @@ void PCPDynamicColumns_init(PCPDynamicColumns* columns) {
|
||||
free(path);
|
||||
}
|
||||
|
||||
static void PCPDynamicColumns_free(ATTR_UNUSED ht_key_t key, void* value, ATTR_UNUSED void* data) {
|
||||
PCPDynamicColumn* column = (PCPDynamicColumn*) value;
|
||||
free(column->metricName);
|
||||
free(column->super.heading);
|
||||
free(column->super.caption);
|
||||
free(column->super.description);
|
||||
}
|
||||
|
||||
void PCPDynamicColumns_done(Hashtable* table) {
|
||||
Hashtable_foreach(table, PCPDynamicColumns_free, NULL);
|
||||
}
|
||||
|
||||
void PCPDynamicColumn_writeField(PCPDynamicColumn* this, const Process* proc, RichString* str) {
|
||||
const PCPProcess* pp = (const PCPProcess*) proc;
|
||||
unsigned int type = PCPMetric_type(this->id);
|
||||
|
@ -26,6 +26,8 @@ typedef struct PCPDynamicColumns_ {
|
||||
|
||||
void PCPDynamicColumns_init(PCPDynamicColumns* columns);
|
||||
|
||||
void PCPDynamicColumns_done(Hashtable* table);
|
||||
|
||||
void PCPDynamicColumn_writeField(PCPDynamicColumn* this, const Process* proc, RichString* str);
|
||||
|
||||
int PCPDynamicColumn_compareByKey(const PCPProcess* p1, const PCPProcess* p2, ProcessField key);
|
||||
|
@ -283,6 +283,22 @@ void PCPDynamicMeters_init(PCPDynamicMeters* meters) {
|
||||
free(path);
|
||||
}
|
||||
|
||||
static void PCPDynamicMeter_free(ATTR_UNUSED ht_key_t key, void* value, ATTR_UNUSED void* data) {
|
||||
PCPDynamicMeter* meter = (PCPDynamicMeter*) value;
|
||||
for (size_t i = 0; i < meter->totalMetrics; i++) {
|
||||
free(meter->metrics[i].name);
|
||||
free(meter->metrics[i].label);
|
||||
free(meter->metrics[i].suffix);
|
||||
}
|
||||
free(meter->metrics);
|
||||
free(meter->super.caption);
|
||||
free(meter->super.description);
|
||||
}
|
||||
|
||||
void PCPDynamicMeters_done(Hashtable* table) {
|
||||
Hashtable_foreach(table, PCPDynamicMeter_free, NULL);
|
||||
}
|
||||
|
||||
void PCPDynamicMeter_enable(PCPDynamicMeter* this) {
|
||||
for (size_t i = 0; i < this->totalMetrics; i++)
|
||||
PCPMetric_enable(this->metrics[i].id, true);
|
||||
|
@ -33,6 +33,8 @@ typedef struct PCPDynamicMeters_ {
|
||||
|
||||
void PCPDynamicMeters_init(PCPDynamicMeters* meters);
|
||||
|
||||
void PCPDynamicMeters_done(Hashtable* table);
|
||||
|
||||
void PCPDynamicMeter_enable(PCPDynamicMeter* this);
|
||||
|
||||
void PCPDynamicMeter_updateValues(PCPDynamicMeter* this, Meter* meter);
|
||||
|
@ -317,7 +317,7 @@ static void PCPProcessList_updateCmdline(Process* process, int pid, int offset,
|
||||
Process_updateComm(process, comm);
|
||||
|
||||
if (PCPMetric_instance(PCP_PROC_EXE, pid, offset, &value, PM_TYPE_STRING)) {
|
||||
Process_updateExe(process, value.cp);
|
||||
Process_updateExe(process, value.cp[0] ? value.cp : NULL);
|
||||
free(value.cp);
|
||||
}
|
||||
}
|
||||
|
@ -363,6 +363,14 @@ void Platform_init(void) {
|
||||
Platform_getMaxPid();
|
||||
}
|
||||
|
||||
void Platform_dynamicColumnsDone(Hashtable* columns) {
|
||||
PCPDynamicColumns_done(columns);
|
||||
}
|
||||
|
||||
void Platform_dynamicMetersDone(Hashtable* meters) {
|
||||
PCPDynamicMeters_done(meters);
|
||||
}
|
||||
|
||||
void Platform_done(void) {
|
||||
pmDestroyContext(pcp->context);
|
||||
if (pcp->result)
|
||||
|
@ -138,6 +138,8 @@ void Platform_gettime_monotonic(uint64_t* msec);
|
||||
|
||||
Hashtable* Platform_dynamicMeters(void);
|
||||
|
||||
void Platform_dynamicMetersDone(Hashtable* meters);
|
||||
|
||||
void Platform_dynamicMeterInit(Meter* meter);
|
||||
|
||||
void Platform_dynamicMeterUpdateValues(Meter* meter);
|
||||
@ -146,6 +148,8 @@ void Platform_dynamicMeterDisplay(const Meter* meter, RichString* out);
|
||||
|
||||
Hashtable* Platform_dynamicColumns(void);
|
||||
|
||||
void Platform_dynamicColumnsDone(Hashtable* columns);
|
||||
|
||||
const char* Platform_dynamicColumnInit(unsigned int key);
|
||||
|
||||
bool Platform_dynamicColumnWriteField(const Process* proc, RichString* str, unsigned int key);
|
||||
|
@ -1,63 +1,47 @@
|
||||
{
|
||||
<ncurses internal memory allocated at startup>
|
||||
Memcheck:Leak
|
||||
match-leak-kinds: reachable
|
||||
...
|
||||
fun:CRT_init
|
||||
fun:main
|
||||
}
|
||||
|
||||
{
|
||||
<ncurses internal memory allocated at startup>
|
||||
Memcheck:Leak
|
||||
match-leak-kinds: reachable
|
||||
...
|
||||
fun:CRT_init
|
||||
}
|
||||
|
||||
{
|
||||
<ncurses internal memory>
|
||||
Memcheck:Leak
|
||||
match-leak-kinds: reachable
|
||||
...
|
||||
fun:wgetch
|
||||
fun:ScreenManager_run
|
||||
fun:Action_runSetup
|
||||
fun:actionSetup
|
||||
fun:MainPanel_eventHandler
|
||||
fun:ScreenManager_run
|
||||
fun:main
|
||||
}
|
||||
|
||||
{
|
||||
<ncurses internal memory>
|
||||
Memcheck:Leak
|
||||
match-leak-kinds: reachable
|
||||
...
|
||||
fun:wgetch
|
||||
fun:ScreenManager_run
|
||||
fun:main
|
||||
}
|
||||
|
||||
{
|
||||
<ncurses internal memory>
|
||||
Memcheck:Leak
|
||||
match-leak-kinds: reachable
|
||||
...
|
||||
fun:wrefresh
|
||||
fun:main
|
||||
}
|
||||
|
||||
{
|
||||
<ncurses internal memory>
|
||||
Memcheck:Leak
|
||||
match-leak-kinds: reachable
|
||||
fun:realloc
|
||||
fun:_nc_doalloc
|
||||
fun:_nc_tparm_analyze
|
||||
fun:tparm
|
||||
match-leak-kinds: possible,reachable
|
||||
...
|
||||
fun:doupdate_sp
|
||||
fun:wrefresh
|
||||
obj:*
|
||||
}
|
||||
|
||||
{
|
||||
<ncurses internal memory>
|
||||
Memcheck:Leak
|
||||
match-leak-kinds: possible,reachable
|
||||
...
|
||||
fun:newterm_sp
|
||||
fun:newterm
|
||||
fun:initscr
|
||||
fun:CRT_init
|
||||
}
|
||||
|
||||
{
|
||||
<ncurses internal memory>
|
||||
Memcheck:Leak
|
||||
match-leak-kinds: reachable
|
||||
...
|
||||
obj:*/libtinfo*
|
||||
fun:CRT_init
|
||||
}
|
||||
|
||||
{
|
||||
<ncurses internal memory>
|
||||
Memcheck:Leak
|
||||
match-leak-kinds: reachable
|
||||
...
|
||||
obj:*/libncurses*
|
||||
fun:CRT_init
|
||||
}
|
||||
|
||||
{
|
||||
<ncurses internal memory>
|
||||
Memcheck:Leak
|
||||
match-leak-kinds: possible,reachable
|
||||
...
|
||||
obj:*/libncurses*
|
||||
fun:CRT_setColors
|
||||
fun:CRT_init
|
||||
}
|
||||
|
@ -3,4 +3,4 @@
|
||||
SCRIPT=$(readlink -f "$0")
|
||||
SCRIPTDIR=$(dirname "$SCRIPT")
|
||||
|
||||
valgrind --leak-check=full --show-reachable=yes --show-leak-kinds=all --track-fds=yes --errors-for-leak-kinds=all --suppressions="${SCRIPTDIR}/htop_suppressions.valgrind" "${SCRIPTDIR}/../htop"
|
||||
valgrind --leak-check=full --show-reachable=yes --show-leak-kinds=all --track-fds=yes --errors-for-leak-kinds=all --track-origins=yes --suppressions="${SCRIPTDIR}/htop_suppressions.valgrind" "${SCRIPTDIR}/../htop"
|
||||
|
@ -131,6 +131,8 @@ IGNORE_WCASTQUAL_END
|
||||
|
||||
static inline Hashtable* Platform_dynamicMeters(void) { return NULL; }
|
||||
|
||||
static inline void Platform_dynamicMetersDone(ATTR_UNUSED Hashtable* table) { }
|
||||
|
||||
static inline void Platform_dynamicMeterInit(ATTR_UNUSED Meter* meter) { }
|
||||
|
||||
static inline void Platform_dynamicMeterUpdateValues(ATTR_UNUSED Meter* meter) { }
|
||||
@ -139,6 +141,8 @@ static inline void Platform_dynamicMeterDisplay(ATTR_UNUSED const Meter* meter,
|
||||
|
||||
static inline Hashtable* Platform_dynamicColumns(void) { return NULL; }
|
||||
|
||||
static inline void Platform_dynamicColumnsDone(ATTR_UNUSED Hashtable* table) { }
|
||||
|
||||
static inline const char* Platform_dynamicColumnInit(ATTR_UNUSED unsigned int key) { return NULL; }
|
||||
|
||||
static inline bool Platform_dynamicColumnWriteField(ATTR_UNUSED const Process* proc, ATTR_UNUSED RichString* str, ATTR_UNUSED unsigned int key) { return false; }
|
||||
|
@ -79,6 +79,8 @@ static inline void Platform_gettime_monotonic(uint64_t* msec) {
|
||||
|
||||
static inline Hashtable* Platform_dynamicMeters(void) { return NULL; }
|
||||
|
||||
static inline void Platform_dynamicMetersDone(ATTR_UNUSED Hashtable* table) { }
|
||||
|
||||
static inline void Platform_dynamicMeterInit(ATTR_UNUSED Meter* meter) { }
|
||||
|
||||
static inline void Platform_dynamicMeterUpdateValues(ATTR_UNUSED Meter* meter) { }
|
||||
@ -87,6 +89,8 @@ static inline void Platform_dynamicMeterDisplay(ATTR_UNUSED const Meter* meter,
|
||||
|
||||
static inline Hashtable* Platform_dynamicColumns(void) { return NULL; }
|
||||
|
||||
static inline void Platform_dynamicColumnsDone(ATTR_UNUSED Hashtable* table) { }
|
||||
|
||||
static inline const char* Platform_dynamicColumnInit(ATTR_UNUSED unsigned int key) { return NULL; }
|
||||
|
||||
static inline bool Platform_dynamicColumnWriteField(ATTR_UNUSED const Process* proc, ATTR_UNUSED RichString* str, ATTR_UNUSED unsigned int key) { return false; }
|
||||
|
Reference in New Issue
Block a user