26 Commits

Author SHA1 Message Date
691b7dd5af Merge branch 'cgzones-readme' 2021-09-20 16:31:31 +10:00
ad0f9c58bf Merge branch 'readme' of https://github.com/cgzones/htop into cgzones-readme 2021-09-20 16:31:24 +10:00
a8d0f2a7fe Merge branch 'cgzones-multicolumn' 2021-09-20 16:30:18 +10:00
e6596cbd69 Merge branch 'multicolumn' of https://github.com/cgzones/htop into cgzones-multicolumn 2021-09-20 16:29:44 +10:00
40c9f89971 Merge branch 'cgzones-mouse' 2021-09-20 16:29:17 +10:00
09ad8067f4 Merge branch 'mouse' of https://github.com/cgzones/htop into cgzones-mouse 2021-09-20 16:29:05 +10:00
bda3ee2b81 Update ReadMe
Add license and repology badge, improve several wording, improve
several sections.

[skip ci]
2021-09-19 14:47:32 +02:00
bdb015ffa3 Header: do not let multi-column meters expand to empty neighbors
Extending to right neighbors is intended for text meters with an
overlong content, so the whole text is shown if possible.
Multi column meters, like the combined memory and swap meter, position
its text depending on the given total width; keep the position to the
original assigned header slot.

Short term resolution for #796
2021-09-18 17:21:32 +02:00
80a515abcc Fix typo 2021-09-18 15:55:21 +02:00
5000cefc13 Fix typo in gettime implementation on darwin 2021-09-18 14:14:23 +02:00
bf7d98e7ac MainPanel: do not reset on disabled mouse events
Do not reset the hidden process selection (and hidden function bar) on mouse events, when mouse support is disabled.
2021-09-18 11:48:32 +02:00
c096712b8d Merge pull request #779 from BenBE/percent-highlight
Highlight percentages similar to large numbers
2021-09-14 12:53:33 +10:00
be82448bd5 Process_printPercentage using one color transition
Update Process_printPercentage such that color change happens only once at 100% and beyond.
2021-09-14 11:16:34 +10:00
68c00b9cdb Header: cache number of columns in HeaderLayout_getColumns
The header layout, and therefore the number of columns, should never
be changed within such loop.
2021-09-12 18:35:24 +02:00
3c8d586a1c Linux: recalculate LRS value
The LRS value is only zero in the first cycle; drop the check to
recalculate while running.
2021-09-12 18:20:40 +02:00
3869c43393 valgrind: rewrite ncurses suppressions
Simplify and update valgrind suppressions for possible leak and
reachable memory inside ncurses.
2021-09-11 00:07:23 +02:00
fcca4c2f2d valgrind: track origin of uninitialised values 2021-09-11 00:07:23 +02:00
dd88510dcd HeaderOptionsPanel: select the current not saved option
Select the current active header layout, not the current saved layout
from the settings, as the value gets only saved back from the active
header to settings on closing the setup menu.

Closes: #785
2021-09-11 00:06:41 +02:00
43ffdb0eda Linux: zero CPU data after allocation
Zero all the CPU data, like totalPeriod, after its memory allocation via
realloc(3).

    Conditional jump or move depends on uninitialised value(s)
       at 0x132A9B: LinuxProcessList_scanCPUTime (LinuxProcessList.c:1928)
       by 0x1358C3: ProcessList_goThroughEntries (LinuxProcessList.c:2079)
        by 0x12A79A: ProcessList_scan (ProcessList.c:627)
        by 0x11CA67: CommandLine_run (CommandLine.c:357)
        by 0x4A81E49: (below main) (libc-start.c:314)
      Uninitialised value was created by a heap allocation
        at 0x48396C5: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
        by 0x12F633: xRealloc (XUtils.c:64)
        by 0x12F633: xReallocArray (XUtils.c:78)
        by 0x1325A8: LinuxProcessList_updateCPUcount (LinuxProcessList.c:207)
        by 0x134E0A: ProcessList_new (LinuxProcessList.c:284)
        by 0x11C8D0: CommandLine_run (CommandLine.c:301)
        by 0x4A81E49: (below main) (libc-start.c:314)
2021-09-11 00:04:00 +02:00
bf395e10c5 Add xReallocArrayZero() helper
Add helper function to reallocate an dynamic allocated array including
zeroing the newly allocated memory.
2021-09-11 00:04:00 +02:00
8f259bc5e1 MemorySwapMeter: align with CPU meter
Use the same width for each sub meter to align with CPU meter.
Currently if the total width is even, so it does not split exactly into
2 equal parts plus 1 (for the middle space character column), the extra
column is added to the second meter width.

Closes: #783
2021-09-10 17:09:48 +02:00
38e6136b82 CRT: close backup stderr file after reset
Close the backup file descriptor of original stderr once it has been
restored at stderr.
2021-09-10 17:01:26 +02:00
589b0733d9 Settings: set ok when reading configuration from sysconfdir
Without this htoprc from sysconfdir is ignored and default meters
are loaded.
2021-09-10 08:58:58 +02:00
c9b58c7fbe Add missing end-of-line to htoprc file version mismatch warning 2021-09-08 12:13:56 +10:00
ca06e68037 Fix a typo in a configure.ac comment 2021-09-08 12:11:58 +10:00
3f805cf347 Highlight large percentages similar to large memory columns 2021-09-05 18:49:05 +02:00
19 changed files with 157 additions and 122 deletions

View File

@ -381,6 +381,7 @@ const MeterClass AllCPUs2Meter_class = {
}, },
.updateValues = AllCPUsMeter_updateValues, .updateValues = AllCPUsMeter_updateValues,
.defaultMode = CUSTOM_METERMODE, .defaultMode = CUSTOM_METERMODE,
.isMultiColumn = true,
.total = 100.0, .total = 100.0,
.attributes = CPUMeter_attributes, .attributes = CPUMeter_attributes,
.name = "AllCPUs2", .name = "AllCPUs2",
@ -401,6 +402,7 @@ const MeterClass LeftCPUsMeter_class = {
}, },
.updateValues = AllCPUsMeter_updateValues, .updateValues = AllCPUsMeter_updateValues,
.defaultMode = CUSTOM_METERMODE, .defaultMode = CUSTOM_METERMODE,
.isMultiColumn = true,
.total = 100.0, .total = 100.0,
.attributes = CPUMeter_attributes, .attributes = CPUMeter_attributes,
.name = "LeftCPUs", .name = "LeftCPUs",
@ -421,6 +423,7 @@ const MeterClass RightCPUsMeter_class = {
}, },
.updateValues = AllCPUsMeter_updateValues, .updateValues = AllCPUsMeter_updateValues,
.defaultMode = CUSTOM_METERMODE, .defaultMode = CUSTOM_METERMODE,
.isMultiColumn = true,
.total = 100.0, .total = 100.0,
.attributes = CPUMeter_attributes, .attributes = CPUMeter_attributes,
.name = "RightCPUs", .name = "RightCPUs",
@ -441,6 +444,7 @@ const MeterClass LeftCPUs2Meter_class = {
}, },
.updateValues = AllCPUsMeter_updateValues, .updateValues = AllCPUsMeter_updateValues,
.defaultMode = CUSTOM_METERMODE, .defaultMode = CUSTOM_METERMODE,
.isMultiColumn = true,
.total = 100.0, .total = 100.0,
.attributes = CPUMeter_attributes, .attributes = CPUMeter_attributes,
.name = "LeftCPUs2", .name = "LeftCPUs2",
@ -461,6 +465,7 @@ const MeterClass RightCPUs2Meter_class = {
}, },
.updateValues = AllCPUsMeter_updateValues, .updateValues = AllCPUsMeter_updateValues,
.defaultMode = CUSTOM_METERMODE, .defaultMode = CUSTOM_METERMODE,
.isMultiColumn = true,
.total = 100.0, .total = 100.0,
.attributes = CPUMeter_attributes, .attributes = CPUMeter_attributes,
.name = "RightCPUs2", .name = "RightCPUs2",
@ -481,6 +486,7 @@ const MeterClass AllCPUs4Meter_class = {
}, },
.updateValues = AllCPUsMeter_updateValues, .updateValues = AllCPUsMeter_updateValues,
.defaultMode = CUSTOM_METERMODE, .defaultMode = CUSTOM_METERMODE,
.isMultiColumn = true,
.total = 100.0, .total = 100.0,
.attributes = CPUMeter_attributes, .attributes = CPUMeter_attributes,
.name = "AllCPUs4", .name = "AllCPUs4",
@ -501,6 +507,7 @@ const MeterClass LeftCPUs4Meter_class = {
}, },
.updateValues = AllCPUsMeter_updateValues, .updateValues = AllCPUsMeter_updateValues,
.defaultMode = CUSTOM_METERMODE, .defaultMode = CUSTOM_METERMODE,
.isMultiColumn = true,
.total = 100.0, .total = 100.0,
.attributes = CPUMeter_attributes, .attributes = CPUMeter_attributes,
.name = "LeftCPUs4", .name = "LeftCPUs4",
@ -521,6 +528,7 @@ const MeterClass RightCPUs4Meter_class = {
}, },
.updateValues = AllCPUsMeter_updateValues, .updateValues = AllCPUsMeter_updateValues,
.defaultMode = CUSTOM_METERMODE, .defaultMode = CUSTOM_METERMODE,
.isMultiColumn = true,
.total = 100.0, .total = 100.0,
.attributes = CPUMeter_attributes, .attributes = CPUMeter_attributes,
.name = "RightCPUs4", .name = "RightCPUs4",
@ -541,6 +549,7 @@ const MeterClass AllCPUs8Meter_class = {
}, },
.updateValues = AllCPUsMeter_updateValues, .updateValues = AllCPUsMeter_updateValues,
.defaultMode = CUSTOM_METERMODE, .defaultMode = CUSTOM_METERMODE,
.isMultiColumn = true,
.total = 100.0, .total = 100.0,
.attributes = CPUMeter_attributes, .attributes = CPUMeter_attributes,
.name = "AllCPUs8", .name = "AllCPUs8",
@ -561,6 +570,7 @@ const MeterClass LeftCPUs8Meter_class = {
}, },
.updateValues = AllCPUsMeter_updateValues, .updateValues = AllCPUsMeter_updateValues,
.defaultMode = CUSTOM_METERMODE, .defaultMode = CUSTOM_METERMODE,
.isMultiColumn = true,
.total = 100.0, .total = 100.0,
.attributes = CPUMeter_attributes, .attributes = CPUMeter_attributes,
.name = "LeftCPUs8", .name = "LeftCPUs8",
@ -581,6 +591,7 @@ const MeterClass RightCPUs8Meter_class = {
}, },
.updateValues = AllCPUsMeter_updateValues, .updateValues = AllCPUsMeter_updateValues,
.defaultMode = CUSTOM_METERMODE, .defaultMode = CUSTOM_METERMODE,
.isMultiColumn = true,
.total = 100.0, .total = 100.0,
.attributes = CPUMeter_attributes, .attributes = CPUMeter_attributes,
.name = "RightCPUs8", .name = "RightCPUs8",

2
CRT.c
View File

@ -772,6 +772,8 @@ static void dumpStderr(void) {
fsync(STDERR_FILENO); fsync(STDERR_FILENO);
dup2(stderrRedirectBackupFd, STDERR_FILENO); dup2(stderrRedirectBackupFd, STDERR_FILENO);
close(stderrRedirectBackupFd);
stderrRedirectBackupFd = -1;
lseek(stderrRedirectNewFd, 0, SEEK_SET); lseek(stderrRedirectNewFd, 0, SEEK_SET);
bool header = false; bool header = false;

View File

@ -212,7 +212,10 @@ void Header_draw(const Header* this) {
Meter* meter = (Meter*) Vector_get(meters, i); Meter* meter = (Meter*) Vector_get(meters, i);
float actualWidth = colWidth; 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++) { 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;
} }

View File

@ -23,7 +23,7 @@ typedef struct Header_ {
int height; int height;
} Header; } 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); Header* Header_new(ProcessList* pl, Settings* settings, HeaderLayout hLayout);

View File

@ -82,6 +82,6 @@ HeaderOptionsPanel* HeaderOptionsPanel_new(Settings* settings, ScreenManager* sc
for (int i = 0; i < LAST_HEADER_LAYOUT; i++) { for (int i = 0; i < LAST_HEADER_LAYOUT; i++) {
Panel_add(super, (Object*) CheckItem_newByVal(HeaderLayout_layouts[i].description, false)); 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; return this;
} }

View File

@ -61,8 +61,8 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
if (ch == KEY_RESIZE) if (ch == KEY_RESIZE)
return IGNORED; return IGNORED;
/* reset on every normal key */ /* reset on every normal key, except mouse events while mouse support is disabled */
if (ch != ERR) if (ch != ERR && (ch != KEY_MOUSE || this->state->settings->enableMouse))
this->state->hideProcessSelection = false; this->state->hideProcessSelection = false;
if (EVENT_IS_HEADER_CLICK(ch)) { if (EVENT_IS_HEADER_CLICK(ch)) {

View File

@ -33,10 +33,14 @@ static void MemorySwapMeter_updateValues(Meter* this) {
static void MemorySwapMeter_draw(Meter* this, int x, int y, int w) { static void MemorySwapMeter_draw(Meter* this, int x, int y, int w) {
MemorySwapMeterData* data = this->meterData; 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); 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); 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) { static void MemorySwapMeter_init(Meter* this) {
@ -91,6 +95,7 @@ const MeterClass MemorySwapMeter_class = {
}, },
.updateValues = MemorySwapMeter_updateValues, .updateValues = MemorySwapMeter_updateValues,
.defaultMode = CUSTOM_METERMODE, .defaultMode = CUSTOM_METERMODE,
.isMultiColumn = true,
.name = "MemorySwap", .name = "MemorySwap",
.uiName = "Memory & Swap", .uiName = "Memory & Swap",
.description = "Combined memory and swap usage", .description = "Combined memory and swap usage",

View File

@ -73,6 +73,7 @@ typedef struct MeterClass_ {
const char* const caption; /* prefix in the actual header */ const char* const caption; /* prefix in the actual header */
const char* const description; /* optional meter description in header setup menu */ const char* const description; /* optional meter description in header setup menu */
const uint8_t maxItems; const uint8_t maxItems;
const bool isMultiColumn; /* whether the meter draws multiple sub-columns (defaults to false) */
} MeterClass; } MeterClass;
#define As_Meter(this_) ((const MeterClass*)((this_)->super.klass)) #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_attributes(this_) As_Meter(this_)->attributes
#define Meter_name(this_) As_Meter(this_)->name #define Meter_name(this_) As_Meter(this_)->name
#define Meter_uiName(this_) As_Meter(this_)->uiName #define Meter_uiName(this_) As_Meter(this_)->uiName
#define Meter_isMultiColumn(this_) As_Meter(this_)->isMultiColumn
typedef struct GraphData_ { typedef struct GraphData_ {
struct timeval time; struct timeval time;

View File

@ -715,8 +715,10 @@ void Process_printPercentage(float val, char* buffer, int n, int* attr) {
} }
xSnprintf(buffer, n, "%4.1f ", val); xSnprintf(buffer, n, "%4.1f ", val);
} else if (val < 999) { } else if (val < 999) {
*attr = CRT_colors[PROCESS_MEGABYTES];
xSnprintf(buffer, n, "%3d. ", (int)val); xSnprintf(buffer, n, "%3d. ", (int)val);
} else { } else {
*attr = CRT_colors[PROCESS_MEGABYTES];
xSnprintf(buffer, n, "%4d ", (int)val); xSnprintf(buffer, n, "%4d ", (int)val);
} }
} else { } else {

100
README
View File

@ -1,10 +1,12 @@
# [![htop](htop.png)](https://htop.dev) # [![htop logo](htop.png)](https://htop.dev)
[![CI](https://github.com/htop-dev/htop/workflows/CI/badge.svg)](https://github.com/htop-dev/htop/actions) [![CI](https://github.com/htop-dev/htop/workflows/CI/badge.svg)](https://github.com/htop-dev/htop/actions)
[![Coverity Scan Build Status](https://scan.coverity.com/projects/21665/badge.svg)](https://scan.coverity.com/projects/21665) [![Coverity Scan Build Status](https://scan.coverity.com/projects/21665/badge.svg)](https://scan.coverity.com/projects/21665)
[![Mailing List](https://img.shields.io/badge/Mailing%20List-htop-blue.svg)](https://groups.io/g/htop) [![Mailing List](https://img.shields.io/badge/Mailing%20List-htop-blue.svg)](https://groups.io/g/htop)
[![IRC #htop](https://img.shields.io/badge/IRC-htop-blue.svg)](https://web.libera.chat/#htop) [![IRC #htop](https://img.shields.io/badge/IRC-htop-blue.svg)](https://web.libera.chat/#htop)
[![Github Release](https://img.shields.io/github/release/htop-dev/htop.svg)](https://github.com/htop-dev/htop/releases/latest) [![GitHub Release](https://img.shields.io/github/release/htop-dev/htop.svg)](https://github.com/htop-dev/htop/releases/latest)
[![Packaging status](https://repology.org/badge/tiny-repos/htop.svg)](https://repology.org/project/htop/versions)
[![License: GPL v2](https://img.shields.io/badge/License-GPL%20v2-blue.svg)](COPYING?raw=true)
![Screenshot of htop](docs/images/screenshot.png?raw=true) ![Screenshot of htop](docs/images/screenshot.png?raw=true)
@ -13,23 +15,27 @@
`htop` is a cross-platform interactive process viewer. `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. `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. 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. 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 ## Build instructions
### Prerequisite ### Prerequisite
List of build-time dependencies: List of build-time dependencies:
* `build-essential` standard GNU autotools-based * standard GNU autotools-based C toolchain
* `autoconf` - C99 compliant compiler
* `autotools` - `autoconf`
* `ncurses` - `autotools`
* `ncurses`
**Note about `ncurses`:** **Note about `ncurses`:**
> htop requires ncurses 6.0. Be aware the appropriate package is sometimes still called libncurses5 (on Debian/Ubuntu). Also ncurses usually comes in two flavours: > htop requires ncurses 6.0. Be aware the appropriate package is sometimes still called libncurses5 (on Debian/Ubuntu). Also ncurses usually comes in two flavours:
@ -42,27 +48,26 @@ List of additional build-time dependencies (based on feature flags):
* `sensors` * `sensors`
* `hwloc` * `hwloc`
* `libcap` * `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** **Debian/Ubuntu**
~~~ shell ~~~ shell
sudo apt install libncursesw5-dev autotools-dev autoconf sudo apt install libncursesw5-dev autotools-dev autoconf build-essential
~~~ ~~~
**Fedora/RHEL** **Fedora/RHEL**
~~~ shell ~~~ shell
sudo dnf install ncurses-devel automake autoconf sudo dnf install ncurses-devel automake autoconf gcc
~~~ ~~~
### Compiling from source: ### Compile 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: 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 ~~~ shell
./autogen.sh && ./configure && make ./autogen.sh && ./configure && make
~~~ ~~~
By default `make install` will install into `/usr/local`, for changing the path use `./configure --prefix=/some/path`.
### Install ### 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`. 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`:
enable Unicode support enable Unicode support
dependency: *libncursesw* - dependency: *libncursesw*
default: *yes* - default: *yes*
* `--enable-pcp`:
enable Performance Co-Pilot support via a new pcp-htop utility
dependency: *libpcp*
default: *no*
* `--enable-affinity`: * `--enable-affinity`:
enable `sched_setaffinity(2)` and `sched_getaffinity(2)` for affinity support; conflicts with hwloc enable `sched_setaffinity(2)` and `sched_getaffinity(2)` for affinity support; conflicts with hwloc
default: *check* - default: *check*
* `--enable-hwloc`: * `--enable-hwloc`:
enable hwloc support for CPU affinity; disables affinity support enable hwloc support for CPU affinity; disables affinity support
dependency: *libhwloc* - dependency: *libhwloc*
default: *no* - default: *no*
* `--enable-static`: * `--enable-static`:
build a static htop binary; hwloc and delay accounting are not supported build a static htop binary; hwloc and delay accounting are not supported
default: *no* - default: *no*
* `--enable-debug`: * `--enable-debug`:
Enable asserts and internal sanity checks; implies a performance penalty 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 #### Linux
* `--enable-sensors`: * `--enable-sensors`:
enable libsensors(3) support for reading temperature data enable libsensors(3) support for reading temperature data
dependencies: *libsensors-dev*(build-time), at runtime *libsensors* is loaded via `dlopen(3)` if available - dependencies: *libsensors-dev*(build-time), at runtime *libsensors* is loaded via `dlopen(3)` if available
default: *check* - default: *check*
* `--enable-capabilities`: * `--enable-capabilities`:
enable Linux capabilities support enable Linux capabilities support
dependency: *libcap* - dependency: *libcap*
default: *check* - default: *check*
* `--with-proc`: * `--with-proc`:
location of a Linux-compatible proc filesystem location of a Linux-compatible proc filesystem
default: */proc* - default: */proc*
* `--enable-openvz`: * `--enable-openvz`:
enable OpenVZ support enable OpenVZ support
default: *no* - default: *no*
* `--enable-vserver`: * `--enable-vserver`:
enable VServer support enable VServer support
default: *no* - default: *no*
* `--enable-ancient-vserver`: * `--enable-ancient-vserver`:
enable ancient VServer support (implies `--enable-vserver`) enable ancient VServer support (implies `--enable-vserver`)
default: *no* - default: *no*
* `--enable-delayacct`: * `--enable-delayacct`:
enable Linux delay accounting support enable Linux delay accounting support
dependencies: *pkg-config*(build-time), *libnl-3* and *libnl-genl-3* - dependencies: *pkg-config*(build-time), *libnl-3* and *libnl-genl-3*
default: *check* - default: *check*
## Runtime dependencies: ## Runtime dependencies:
@ -128,20 +136,22 @@ To install on the local system run `make install`. By default `make install` ins
### Runtime optional dependencies: ### Runtime optional dependencies:
`htop` has a set of fixed optional dependencies, depending on build/configure option used: `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`. * `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`. * `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. `htop` checks for the availability of the actual runtime lib as `htop` runs.
**BSD** #### BSD
On most *BSD systems you also have `kvm` as a static requirement to read all the kernel information. 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). More information on required and optional dependencies can be found in [configure.ac](configure.ac).
## Usage ## 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 ## 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. 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 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 location in the source code you are referring to and a possible fix. 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 ## 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. In 2020 a [team](https://github.com/orgs/htop-dev/people) took over the development amicably and continues to maintain `htop` collaboratively.

View File

@ -181,7 +181,7 @@ static bool Settings_read(Settings* this, const char* fileName, unsigned int ini
this->config_version = atoi(option[1]); this->config_version = atoi(option[1]);
if (this->config_version > CONFIG_READER_MIN_VERSION) { if (this->config_version > CONFIG_READER_MIN_VERSION) {
// the version of the config file on disk is newer than what we can read // 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); fprintf(stderr, " The configuration version will be downgraded to v%d when %s exits.\n", CONFIG_READER_MIN_VERSION, PACKAGE);
String_freeArray(option); String_freeArray(option);
fclose(fd); fclose(fd);
@ -542,7 +542,7 @@ Settings* Settings_new(unsigned int initialCpuCount, Hashtable* dynamicColumns)
} }
if (!ok) { if (!ok) {
this->changed = true; this->changed = true;
Settings_read(this, SYSCONFDIR "/htoprc", initialCpuCount); ok = Settings_read(this, SYSCONFDIR "/htoprc", initialCpuCount);
} }
if (!ok) { if (!ok) {
Settings_defaultMeters(this, initialCpuCount); Settings_defaultMeters(this, initialCpuCount);

View File

@ -78,6 +78,22 @@ void* xReallocArray(void* ptr, size_t nmemb, size_t size) {
return xRealloc(ptr, nmemb * 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) { inline bool String_contains_i(const char* s1, const char* s2) {
return strcasestr(s1, s2) != NULL; return strcasestr(s1, s2) != NULL;
} }

View File

@ -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* 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 * String_startsWith gives better performance if strlen(match) can be computed
* at compile time (e.g. when they are immutable string literals). :) * at compile time (e.g. when they are immutable string literals). :)

View File

@ -367,7 +367,7 @@ if test "$enable_static" = yes; then
fi fi
if test "$my_htop_platform" = "solaris"; then if test "$my_htop_platform" = "solaris"; then
# On OmniOS /usr/include/sys/regset.h redefines ERR to 13 - \r, breaking the Enter key. # 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.]) AC_DEFINE([ERR], [(-1)], [Predefine ncurses macro.])
fi fi
AC_CHECK_FUNCS( [set_escdelay] ) AC_CHECK_FUNCS( [set_escdelay] )

View File

@ -269,7 +269,7 @@ ERROR_A:
Process_updateCmdline(proc, k->kp_proc.p_comm, 0, strlen(k->kp_proc.p_comm)); 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) { static long long int nanosecondsToCentiseconds(uint64_t nanoseconds) {
const uint64_t centiseconds_per_second = 100; const uint64_t centiseconds_per_second = 100;
const uint64_t nanoseconds_per_second = 1e9; const uint64_t nanoseconds_per_second = 1e9;

View File

@ -432,7 +432,8 @@ void Platform_gettime_monotonic(uint64_t* msec) {
#else #else
Generic_gettime_monotomic(msec); Generic_gettime_monotonic(msec);
#endif #endif
} }

View File

@ -167,11 +167,11 @@ static void LinuxProcessList_updateCPUcount(ProcessList* super) {
DIR* dir = opendir("/sys/devices/system/cpu"); DIR* dir = opendir("/sys/devices/system/cpu");
if (!dir) { if (!dir) {
super->activeCPUs = 1; this->cpuData = xReallocArrayZero(this->cpuData, super->existingCPUs ? (super->existingCPUs + 1) : 0, 2, sizeof(CPUData));
super->existingCPUs = 1;
this->cpuData = xReallocArray(this->cpuData, 2, sizeof(CPUData));
this->cpuData[0].online = true; /* average is always "online" */ this->cpuData[0].online = true; /* average is always "online" */
this->cpuData[1].online = true; this->cpuData[1].online = true;
super->activeCPUs = 1;
super->existingCPUs = 1;
return; return;
} }
@ -204,10 +204,7 @@ static void LinuxProcessList_updateCPUcount(ProcessList* super) {
/* readdir() iterates with no specific order */ /* readdir() iterates with no specific order */
unsigned int max = MAXIMUM(existing, id + 1); unsigned int max = MAXIMUM(existing, id + 1);
if (max > currExisting) { if (max > currExisting) {
this->cpuData = xReallocArray(this->cpuData, max + /* aggregate */ 1, sizeof(CPUData)); this->cpuData = xReallocArrayZero(this->cpuData, currExisting ? (currExisting + 1) : 0, max + /* aggregate */ 1, sizeof(CPUData));
for (unsigned int j = currExisting; j < max; j++) {
this->cpuData[j].online = false;
}
this->cpuData[0].online = true; /* average is always "online" */ this->cpuData[0].online = true; /* average is always "online" */
currExisting = max; currExisting = max;
} }
@ -1426,7 +1423,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_
{ {
bool prev = proc->usesDeletedLib; 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)) { (settings->highlightDeletedExe && !proc->procExeDeleted && !proc->isKernelThread && !proc->isUserlandThread)) {
// Check if we really should recalculate the M_LRS value for this process // 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 = pl->realtimeMs - lp->last_mlrs_calctime;

View File

@ -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> <ncurses internal memory>
Memcheck:Leak Memcheck:Leak
match-leak-kinds: reachable match-leak-kinds: possible,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
... ...
fun:doupdate_sp fun:doupdate_sp
fun:wrefresh 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
} }

View File

@ -3,4 +3,4 @@
SCRIPT=$(readlink -f "$0") SCRIPT=$(readlink -f "$0")
SCRIPTDIR=$(dirname "$SCRIPT") 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"