Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions BacktraceScreen.c
Original file line number Diff line number Diff line change
Expand Up @@ -322,12 +322,14 @@ static void BacktracePanelRow_displayInformation(const Object* super, RichString
const char* processName = "";
if (process->mergedCommand.str) {
processName = process->mergedCommand.str;
for (size_t i = 0; i < process->mergedCommand.highlightCount; i++) {
const ProcessCmdlineHighlight* highlight = process->mergedCommand.highlights;
if (highlight->flags & CMDLINE_HIGHLIGHT_FLAG_BASENAME) {
highlightLen = highlight->length;
highlightOffset = highlight->offset;
break;
if (process->mergedCommand.highlights) {
for (size_t i = 0; i < process->mergedCommand.highlightCount; i++) {
const ProcessCmdlineHighlight* highlight = &process->mergedCommand.highlights[i];
if (highlight->flags & CMDLINE_HIGHLIGHT_FLAG_BASENAME) {
highlightLen = highlight->length;
highlightOffset = highlight->offset;
break;
}
Comment on lines -325 to +332

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This doesn't make much sense, as the loop will be skipped regardless. So you are reading more memory, just to do something that a non-broken compiler has been doing since the 1980s … Zero-exec loops were in the standard from the beginning and for maintainability we'll not be catering to broken compilers. Not to mention, that this optimization will likely cause more trouble than it benefits performance, as the likely case with this loop in particular is at least one execution …

}
}
} else if (process->cmdline) {
Expand Down
6 changes: 6 additions & 0 deletions CRT.c
Original file line number Diff line number Diff line change
Expand Up @@ -1263,6 +1263,7 @@ IGNORE_WCASTQUAL_BEGIN
}
define_key("\033[I", KEY_FOCUS_IN);
define_key("\033[O", KEY_FOCUS_OUT);
full_write_str(STDOUT_FILENO, "\033[?1004h");
Comment on lines 1264 to +1266

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Benefit of forcing this active?

#ifdef HTOP_NETBSD
IGNORE_WCASTQUAL_END
#undef define_key
Expand Down Expand Up @@ -1318,6 +1319,11 @@ void CRT_done(void) {
curs_set(1);
endwin();

const char* termType = getenv("TERM");
if (terminalSupportsDefinedKeys(termType)) {
full_write_str(STDOUT_FILENO, "\033[?1004l");
}

dumpStderr();
}

Expand Down
2 changes: 2 additions & 0 deletions CommandLine.c
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,8 @@ int CommandLine_run(int argc, char** argv) {
if (settings->ss->allBranchesCollapsed)
Table_collapseAllBranches(&pt->super);

xTrimMemory();

ScreenManager_run(scr, NULL, NULL, NULL);

Platform_done();
Expand Down
23 changes: 19 additions & 4 deletions Hashtable.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ in the source distribution for its full text.

typedef struct HashtableItem_ {
ht_key_t key;
size_t probe;
unsigned int probe;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Contradicts one intended direction we want to go with the Hashtable code for quite some time. Using size_t is intentional. Problem why this hasn't been done throughout the Hashtable code yet is related to the amount of affected code when this is changed.

void* value;
} HashtableItem;

Expand All @@ -48,7 +48,7 @@ static void Hashtable_dump(const Hashtable* this) {

size_t items = 0;
for (size_t i = 0; i < this->size; i++) {
fprintf(stderr, " item %5zu: key = %5u probe = %2zu value = %p\n",
fprintf(stderr, " item %5zu: key = %5u probe = %2u value = %p\n",
i,
this->buckets[i].key,
this->buckets[i].probe,
Expand Down Expand Up @@ -151,7 +151,7 @@ void Hashtable_clear(Hashtable* this) {

static void insert(Hashtable* this, ht_key_t key, void* value) {
size_t index = key % this->size;
size_t probe = 0;
unsigned int probe = 0;
#ifndef NDEBUG
size_t origIndex = index;
#endif
Expand Down Expand Up @@ -299,9 +299,24 @@ void* Hashtable_remove(Hashtable* this, ht_key_t key) {
return res;
}

void Hashtable_shrink(Hashtable* this) {
assert(Hashtable_isConsistent(this));

/* shrink on load-factor < 0.4 if size can be reduced */
if (this->size > 61 && 10 * this->items < 4 * this->size) {
size_t targetSize = this->items ? (this->items * 2) : 13;
size_t newSize = nextPrime(targetSize);
if (newSize < this->size) {
Hashtable_setSize(this, newSize);
}
}

assert(Hashtable_isConsistent(this));
}

Comment on lines +302 to +316

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

There's related PRs to this. Don't mix concerns between PRs.

void* Hashtable_get(Hashtable* this, ht_key_t key) {
size_t index = key % this->size;
size_t probe = 0;
unsigned int probe = 0;
void* res = NULL;
#ifndef NDEBUG
size_t origIndex = index;
Expand Down
2 changes: 2 additions & 0 deletions Hashtable.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ void Hashtable_put(Hashtable* this, ht_key_t key, void* value);

void* Hashtable_remove(Hashtable* this, ht_key_t key);

void Hashtable_shrink(Hashtable* this);

void* Hashtable_get(Hashtable* this, ht_key_t key);

void Hashtable_foreach(Hashtable* this, Hashtable_PairFunction f, void* userData);
Expand Down
2 changes: 2 additions & 0 deletions Machine.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ void Machine_init(Machine* this, UsersTable* usersTable, uid_t userId) {

this->htopUserId = getuid();

this->inBackground = false;

// discover fixed column width limits
Row_setPidColumnWidth(Platform_getMaxPid());

Expand Down
2 changes: 2 additions & 0 deletions Machine.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ typedef struct Machine_ {
Table **tables;
Table *activeTable;
Table *processTable;

bool inBackground;
} Machine;


Expand Down
13 changes: 7 additions & 6 deletions Meter.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ in the source distribution for its full text.
#include "Object.h"


#define METER_TXTBUFFER_LEN 256
#define METER_TXTBUFFER_LEN 127

@coderabbitai coderabbitai Bot Oct 15, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🔎 Supported by static analysis

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect all txtBuffer references and their surrounding code.
rg -n -C 4 'txtBuffer' --glob '*.[ch]' .

Repository: htop-dev/htop

Length of output: 22576


🏁 Script executed:

set -euo pipefail
printf '%s\n' '--- diff for Meter.h ---'
git diff 44a59cf1b407df3a1853e9f4f86c3f366808c9e0 cd27007d45adf30209f0fdcf007238ab2c638b2c -- Meter.h

printf '%s\n' '--- buffer helper definitions ---'
rg -n -C 8 -F -e 'METER_BUFFER_CHECK' -e 'METER_BUFFER_APPEND_CHR' -e 'Meter_humanUnit' --glob '*.[ch]' .

printf '%s\n' '--- network writer and input declarations ---'
sed -n '1,155p' NetworkIOMeter.c
rg -n -C 5 -F -e 'cached_rxb_diff_str' -e 'cached_txb_diff_str' --glob '*.[ch]' .

printf '%s\n' '--- pressure writer and file source ---'
sed -n '1,90p' linux/PressureStallMeter.c
rg -n -C 6 -F -e 'PressureStallMeter_updateValues' -e 'const char* file' --glob '*.[ch]' linux .

printf '%s\n' '--- PCP writer block ---'
sed -n '280,370p' pcp/PCPDynamicMeter.c

printf '%s\n' '--- other variable string writers ---'
sed -n '1,215p' linux/ZswapMeter.c
sed -n '160,205p' linux/OpenRCMeter.c
sed -n '295,322p' linux/SystemdMeter.c
sed -n '1,40p' SysArchMeter.c

Repository: htop-dev/htop

Length of output: 42613


🏁 Script executed:

set -euo pipefail
printf '%s\n' '--- complete PCP update function ---'
sed -n '315,430p' pcp/PCPDynamicMeter.c

printf '%s\n' '--- PCP dynamic meter declarations and parser ---'
rg -n -C 8 -F -e 'totalMetrics' -e 'PCPDynamicMetric' -e 'PCPDynamicMeter_scan' -e 'pmsprintf' --glob '*.[ch]' pcp

printf '%s\n' '--- bounded helper definitions ---'
rg -n -C 10 -F -e 'xSnprintf' -e 'String_safeStrncpy' --glob '*.[ch]' XUtils.* String.* pcp | head -260

Repository: htop-dev/htop

Length of output: 41596


🌐 Web query:

official Performance Co-Pilot pmsprintf return value truncation contract

💡 Result:

**The documented PCP contract is not the usual `snprintf` truncation signal.** If the buffer is too small, `pmsprintf` leaves a null-terminated truncated string in `str`; on success it returns the number of characters actually written (excluding the null terminator). The return value is always nonnegative, so don’t rely on `return >= size` to detect truncation. ([linuxman7.org](https://linuxman7.org/linux/man-pages/man3/pmsprintf.3.html))

If the underlying `vsnprintf` fails, `str` is set to an empty null-terminated string and the function returns `0`. ([linuxman7.org](https://linuxman7.org/linux/man-pages/man3/pmsprintf.3.html))

The current upstream header independently describes the null-termination guarantee, but doesn’t specify a separate truncation indicator. ([github.com](https://github.com/performancecopilot/pcp/blob/main/src/include/pcp/pmapi.h))

Citations:

- 1: https://linuxman7.org/linux/man-pages/man3/pmsprintf.3.html
- 2: https://linuxman7.org/linux/man-pages/man3/pmsprintf.3.html
- 3: https://github.com/performancecopilot/pcp/blob/main/src/include/pcp/pmapi.h

🏁 Script executed:

set -euo pipefail
sed -n '470,530p' Meter.c
sed -n '285,310p' XUtils.c

Repository: htop-dev/htop

Length of output: 2045


🏁 Script executed:

set -euo pipefail
rg -n -C 8 -F -e 'void fail' -e 'fail(void)' -e 'fail();' --glob '*.[ch]' .

Repository: htop-dev/htop

Length of output: 5995


Guard Meter_humanUnit against insufficient remaining capacity.

PCPDynamicMeter_updateValues can leave too little space for a later human-readable value. Meter_humanUnit calls xSnprintf, and xSnprintf calls fail() when the formatted value does not fit. A long preceding metric can therefore abort htop instead of producing controlled output.

Check the remaining capacity before formatting each value, then roll back or stop when the value cannot fit.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This one should be left alone in general, as these buffer sizes are used throughout the code …

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@BenBE Thanks for the guidance. I won’t press this recommendation, given the shared use of these buffer sizes.


✏️ Learnings added
Learnt from: BenBE
Repo: htop-dev/htop PR: 2135
File: Meter.h:22-22
Timestamp: 2026-10-16T18:45:09.042Z
Learning: In htop, METER_TXTBUFFER_LEN in Meter.h controls a buffer used throughout the meter code. The maintainer prefers to leave these shared buffer sizes and their handling unchanged in general; avoid isolated recommendations without considering all users.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

You are interacting with an AI system.

#define MAX_METER_GRAPHDATA_VALUES 32768

#define METER_BUFFER_CHECK(buffer, size, written) \
Expand Down Expand Up @@ -118,17 +118,18 @@ struct Meter_ {
const Machine* host;

char* caption;
const int* curAttributes;
double* values;
double total;
void* meterData;
GraphData drawData;

MeterModeId mode;
unsigned int param;
GraphData drawData;
int h;
int columnWidthCount; /**< only used internally by the Header */
uint8_t curItems;
const int* curAttributes;
char txtBuffer[METER_TXTBUFFER_LEN];
double* values;
double total;
void* meterData;
Comment on lines +121 to -131

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

There's also some PR for this kind of changes. Please don't mix concerns between PRs.

};

typedef enum {
Expand Down
Loading
Loading