Skip to content

Commit e6be38c

Browse files
MrCirdoExplorer09BenBE
committed
Demangling support in BacktraceScreen
Add support of demangling symbol names in the backtrace screen. Two backends are supported: libiberty (GNU) and libdemangle (Solaris). [The initial code for demangling support with libiberty was written by Odric. Kang-Che expanded the code to support libdemangle. Benny provided code reviewes and minor fixes.] Co-authored-by: Kang-Che Sung <explorer09@gmail.com> Co-authored-by: Benny Baumann <BenBE@geshi.org>
1 parent 58c6f67 commit e6be38c

7 files changed

Lines changed: 154 additions & 3 deletions

File tree

BacktraceScreen.c

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,34 +34,45 @@ in the source distribution for its full text.
3434
#if defined(HAVE_BACKTRACE_SCREEN)
3535

3636
static const char* const BacktraceScreenFunctions[] = {
37+
#if defined(HAVE_DEMANGLING)
38+
"Mangle ",
39+
#endif
3740
"Full Path",
3841
"Refresh",
3942
"Done ",
4043
NULL
4144
};
4245

4346
static const char* const BacktraceScreenKeys[] = {
47+
#if defined(HAVE_DEMANGLING)
48+
"F2",
49+
#endif
4450
"F3",
4551
"F5",
4652
"Esc",
4753
NULL
4854
};
4955

5056
static const int BacktraceScreenEvents[] = {
57+
#if defined(HAVE_DEMANGLING)
58+
KEY_F(2),
59+
#endif
5160
KEY_F(3),
5261
KEY_F(5),
5362
27,
5463
};
5564

5665
typedef enum BacktraceScreenDisplayOptions_ {
57-
SHOW_FULL_PATH_OBJECT = 1 << 0,
66+
DEMANGLE_NAME_FUNCTION = 1 << 0,
67+
SHOW_FULL_PATH_OBJECT = 1 << 1,
5868
} BacktraceScreenDisplayOptions;
5969

6070
BacktraceFrameData* BacktraceFrameData_new(void) {
6171
BacktraceFrameData* this = AllocThis(BacktraceFrameData);
6272
this->address = 0;
6373
this->offset = 0;
6474
this->functionName = NULL;
75+
this->demangleFunctionName = NULL;
6576
this->objectPath = NULL;
6677
this->index = 0;
6778
this->isSignalFrame = false;
@@ -71,6 +82,7 @@ BacktraceFrameData* BacktraceFrameData_new(void) {
7182
void BacktraceFrameData_delete(Object* object) {
7283
BacktraceFrameData* this = (BacktraceFrameData*)object;
7384
free(this->functionName);
85+
free(this->demangleFunctionName);
7486
free(this->objectPath);
7587
free(this);
7688
}
@@ -79,6 +91,8 @@ static void BacktracePanel_displayHeader(BacktracePanel* this) {
7991
const BacktracePanelPrintingHelper* printingHelper = &this->printingHelper;
8092
const int displayOptions = this->displayOptions;
8193

94+
bool showDemangledNames = (displayOptions & DEMANGLE_NAME_FUNCTION) && printingHelper->hasDemangledNames;
95+
8296
bool showFullPathObject = !!(displayOptions & SHOW_FULL_PATH_OBJECT);
8397
size_t maxObjLen = showFullPathObject ? printingHelper->maxObjPathLen : printingHelper->maxObjNameLen;
8498

@@ -95,7 +109,7 @@ static void BacktracePanel_displayHeader(BacktracePanel* this) {
95109
(int)printingHelper->maxFrameNumLen, "#",
96110
(int)(printingHelper->maxAddrLen + strlen("0x")), "ADDRESS",
97111
(int)maxObjLen, "FILE",
98-
"NAME"
112+
(showDemangledNames ? "NAME (demangled)" : "NAME")
99113
);
100114

101115
Panel_setHeader((Panel*)this, line);
@@ -112,12 +126,17 @@ static void BacktracePanel_makePrintingHelper(const BacktracePanel* this, Backtr
112126
unsigned int maxFrameNum = 0;
113127
size_t longestAddress = 0;
114128

129+
printingHelper->hasDemangledNames = false;
130+
115131
for (int i = 0; i < Vector_size(lines); i++) {
116132
const BacktracePanelRow* row = (const BacktracePanelRow*)Vector_get(lines, i);
117133
if (row->type != BACKTRACE_PANEL_ROW_DATA_FRAME) {
118134
continue;
119135
}
120136

137+
if (row->data.frame->demangleFunctionName)
138+
printingHelper->hasDemangledNames = true;
139+
121140
if (row->data.frame->objectPath) {
122141
const char* objectName = getBasename(row->data.frame->objectPath);
123142
size_t objectNameLength = strlen(objectName);
@@ -193,6 +212,18 @@ static HandlerResult BacktracePanel_eventHandler(Panel* super, int ch) {
193212
HandlerResult result = IGNORED;
194213

195214
switch (ch) {
215+
#if defined(HAVE_DEMANGLING)
216+
case KEY_F(2):
217+
*displayOptions ^= DEMANGLE_NAME_FUNCTION;
218+
219+
bool showDemangledNames = !!(*displayOptions & DEMANGLE_NAME_FUNCTION);
220+
FunctionBar_setLabel(super->defaultBar, KEY_F(2), showDemangledNames ? "Mangle " : "Demangle");
221+
222+
this->super.needsRedraw = true;
223+
BacktracePanel_displayHeader(this);
224+
break;
225+
#endif
226+
196227
case 'p':
197228
case KEY_F(3):
198229
*displayOptions ^= SHOW_FULL_PATH_OBJECT;
@@ -222,9 +253,11 @@ BacktracePanel* BacktracePanel_new(Vector* processes, const Settings* settings)
222253
this->printingHelper.maxFrameNumLen = strlen("#");
223254
this->printingHelper.maxObjNameLen = strlen("FILE");
224255
this->printingHelper.maxObjPathLen = strlen("FILE");
256+
this->printingHelper.hasDemangledNames = false;
225257

226258
this->settings = settings;
227259
this->displayOptions =
260+
DEMANGLE_NAME_FUNCTION |
228261
(settings->showProgramPath ? SHOW_FULL_PATH_OBJECT : 0) |
229262
0;
230263

@@ -331,7 +364,12 @@ static void BacktracePanelRow_displayFrame(const Object* super, RichString* out)
331364

332365
const BacktraceFrameData* frame = row->data.frame;
333366

334-
const char* functionName = frame->functionName ? frame->functionName : "???";
367+
const char* functionName = "???";
368+
if ((displayOptions & DEMANGLE_NAME_FUNCTION) && frame->demangleFunctionName) {
369+
functionName = frame->demangleFunctionName;
370+
} else if (frame->functionName) {
371+
functionName = frame->functionName;
372+
}
335373

336374
char* completeFunctionName = NULL;
337375
xAsprintf(&completeFunctionName, "%s+0x%zx", functionName, frame->offset);

BacktraceScreen.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ typedef struct BacktraceFrameData_ {
2222
size_t address;
2323
size_t offset;
2424
char* functionName;
25+
char* demangleFunctionName;
2526
char* objectPath;
2627
unsigned int index;
2728
bool isSignalFrame;
@@ -32,6 +33,7 @@ typedef struct BacktracePanelPrintingHelper_ {
3233
size_t maxFrameNumLen;
3334
size_t maxObjPathLen;
3435
size_t maxObjNameLen;
36+
bool hasDemangledNames;
3537
} BacktracePanelPrintingHelper;
3638

3739
typedef struct BacktracePanel_ {

Makefile.am

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,11 @@ myhtopheaders += \
172172
myhtopsources += \
173173
BacktraceScreen.c \
174174
generic/UnwindPtrace.c
175+
176+
if HAVE_DEMANGLING
177+
myhtopheaders += generic/Demangle.h
178+
myhtopsources += generic/Demangle.c
179+
endif
175180
endif
176181

177182
# Linux

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,12 @@ To install on the local system run `make install`. By default `make install` ins
120120
- default: *no*
121121
- possible values:
122122
- unwind-ptrace: use **libunwind-ptrace** to get backtraces
123+
* `--enable-demangling`:
124+
enable demangling support for backtraces
125+
- default: *check*
126+
- possible values:
127+
- libiberty: use **libiberty** (GNU) to demangle function names
128+
- libdemangle: use **libdemangle** (Solaris) to demangle function names
123129
* `--enable-static`:
124130
build a static htop binary; hwloc and delay accounting are not supported
125131
- default: *no*

generic/Demangle.c

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
htop - generic/Demangle.c
3+
(C) 2025 htop dev team
4+
Released under the GNU GPLv2+, see the COPYING file
5+
in the source distribution for its full text.
6+
*/
7+
8+
#include "config.h" // IWYU pragma: keep
9+
10+
#include "generic/Demangle.h"
11+
12+
#include <stdbool.h> // IWYU pragma: keep
13+
#include <stddef.h> // IWYU pragma: keep
14+
#include <stdint.h> // IWYU pragma: keep
15+
#include <stdlib.h> // IWYU pragma: keep
16+
#include <string.h> // IWYU pragma: keep
17+
18+
#include "generic/ProvideDemangle.h"
19+
20+
21+
#ifdef HAVE_DEMANGLING
22+
char* Demangle_demangle(const char* mangled) {
23+
# if defined(HAVE_LIBIBERTY_CPLUS_DEMANGLE)
24+
// The default in htop is to show as many details from the mangled
25+
// name as possible.
26+
int options = DMGL_AUTO |
27+
DMGL_TYPES |
28+
DMGL_VERBOSE /* Includes "disambiguators" of Rust crates */ |
29+
DMGL_PARAMS;
30+
31+
return cplus_demangle(mangled, options);
32+
# elif defined(HAVE_LIBDEMANGLE_CPLUS_DEMANGLE)
33+
// The cplus_demangle() API from libdemangle is flawed. It does not
34+
// provide us the required size of the buffer to store the demangled
35+
// name, and it leaves the buffer content undefined if the specified
36+
// buffer size is not big enough.
37+
38+
// No crash on allocation failure. This is for safety against
39+
// incredibly long demangled names in untrustable programs.
40+
static size_t bufSize = 128;
41+
static char* buf;
42+
43+
while (true) {
44+
if (!buf) {
45+
buf = malloc(bufSize);
46+
if (!buf)
47+
return NULL;
48+
}
49+
50+
int res = cplus_demangle(mangled, buf, bufSize);
51+
if (res == 0)
52+
break;
53+
if (res != DEMANGLE_ESPACE || bufSize > SIZE_MAX / 2)
54+
return NULL;
55+
56+
bufSize *= 2;
57+
free(buf);
58+
buf = NULL;
59+
}
60+
61+
return strdup(buf);
62+
# else
63+
(void)mangled;
64+
return NULL;
65+
# endif
66+
}
67+
#endif /* HAVE_DEMANGLING */

generic/Demangle.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#ifndef HEADER_Demangle
2+
#define HEADER_Demangle
3+
/*
4+
htop - generic/Demangle.h
5+
(C) 2025 htop dev team
6+
Released under the GNU GPLv2+, see the COPYING file
7+
in the source distribution for its full text.
8+
*/
9+
10+
#include "Macros.h"
11+
12+
13+
#ifdef HAVE_DEMANGLING
14+
ATTR_NONNULL ATTR_MALLOC
15+
char* Demangle_demangle(const char* mangled);
16+
#else
17+
ATTR_NONNULL
18+
static inline char* Demangle_demangle(const char* mangled) {
19+
(void)mangled;
20+
return NULL;
21+
}
22+
#endif /* HAVE_DEMANGLING */
23+
24+
#endif

generic/UnwindPtrace.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ in the source distribution for its full text.
2323
#include <libunwind.h>
2424
#endif
2525

26+
#ifdef HAVE_DEMANGLING
27+
#include "generic/Demangle.h"
28+
#endif
29+
2630

2731
#ifdef HAVE_LIBUNWIND_PTRACE
2832
static int ptraceAttach(pid_t pid) {
@@ -120,6 +124,11 @@ void UnwindPtrace_makeBacktrace(Vector* frames, pid_t pid, char** error) {
120124
if (unw_get_proc_name(&cursor, buffer, sizeof(buffer), &offset) == 0) {
121125
frame->offset = offset;
122126
frame->functionName = xStrndup(buffer, sizeof(buffer));
127+
128+
# if defined(HAVE_DEMANGLING)
129+
char* demangledName = Demangle_demangle(frame->functionName);
130+
frame->demangleFunctionName = demangledName;
131+
# endif
123132
}
124133
Vector_add(frames, (Object *)frame);
125134
index++;

0 commit comments

Comments
 (0)