Skip to content

Commit cfe5300

Browse files
committed
Add backtrace screen
1 parent 66d43d3 commit cfe5300

6 files changed

Lines changed: 292 additions & 0 deletions

File tree

Action.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ in the source distribution for its full text.
1515
#include <stdlib.h>
1616
#include <string.h>
1717

18+
#include "BacktraceScreen.h"
1819
#include "CRT.h"
1920
#include "CategoriesPanel.h"
2021
#include "CommandScreen.h"
@@ -595,6 +596,27 @@ static Htop_Reaction actionShowLocks(State* st) {
595596
return HTOP_REFRESH | HTOP_REDRAW_BAR;
596597
}
597598

599+
#if (defined(HTOP_LINUX) && defined(HAVE_LIBUNWIND_PTRACE))
600+
static Htop_Reaction actionBacktrace(State *st) {
601+
const Process* process = (Process*) Panel_getSelected((Panel*)st->mainPanel);
602+
if (!process)
603+
return HTOP_OK;
604+
605+
BacktracePanel *panel = BacktracePanel_new(process);
606+
ScreenManager *screenManager = ScreenManager_new(NULL, st->host, st, false);
607+
ScreenManager_add(screenManager, (Panel *)panel, 0);
608+
609+
Panel *lastFocusPanel = NULL;
610+
int lastKey = 0;
611+
612+
ScreenManager_run(screenManager, &lastFocusPanel, &lastKey, NULL);
613+
BacktracePanel_delete((Object *)panel);
614+
ScreenManager_delete(screenManager);
615+
616+
return HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
617+
}
618+
#endif
619+
598620
static Htop_Reaction actionStrace(State* st) {
599621
if (!Action_writeableProcess(st))
600622
return HTOP_OK;
@@ -679,6 +701,10 @@ static const struct {
679701
#if (defined(HAVE_LIBHWLOC) || defined(HAVE_AFFINITY))
680702
{ .key = " a: ", .roInactive = true, .info = "set CPU affinity" },
681703
#endif
704+
705+
#if (defined(HTOP_LINUX) && defined(HAVE_LIBUNWIND_PTRACE))
706+
{ .key = " b: ", .roInactive = false, .info = "show the backtrace of user process" },
707+
#endif
682708
{ .key = " e: ", .roInactive = false, .info = "show process environment" },
683709
{ .key = " i: ", .roInactive = true, .info = "set IO priority" },
684710
{ .key = " l: ", .roInactive = true, .info = "list open files with lsof" },
@@ -918,6 +944,9 @@ void Action_setBindings(Htop_Action* keys) {
918944
keys['\\'] = actionIncFilter;
919945
keys[']'] = actionHigherPriority;
920946
keys['a'] = actionSetAffinity;
947+
#if (defined(HTOP_LINUX) && defined(HAVE_LIBUNWIND_PTRACE))
948+
keys['b'] = actionBacktrace;
949+
#endif
921950
keys['c'] = actionTagAllChildren;
922951
keys['e'] = actionShowEnvScreen;
923952
keys['h'] = actionHelp;

BacktraceScreen.c

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
/*
2+
htop - Action.c
3+
(C) 2015 Hisham H. Muhammad
4+
Released under the GNU GPLv2+, see the COPYING file
5+
in the source distribution for its full text.
6+
*/
7+
8+
#include "BacktraceScreen.h"
9+
10+
#if (defined(HTOP_LINUX) && defined(HAVE_LIBUNWIND_PTRACE))
11+
12+
#include <sys/wait.h>
13+
14+
#include "CRT.h"
15+
#include "Object.h"
16+
#include "Panel.h"
17+
#include "Process.h"
18+
#include "RichString.h"
19+
#include "XUtils.h"
20+
#include "errno.h"
21+
22+
#include <libiberty/demangle.h>
23+
#include <libunwind-ptrace.h>
24+
#include <sys/ptrace.h>
25+
26+
#define MAX_FRAME 256
27+
28+
static const char* const BacktraceScreenFunctions[] = {"Done ", NULL};
29+
30+
static const char* const BacktraceScreenKeys[] = {"Esc"};
31+
32+
static const int BacktraceScreenEvents[] = {27};
33+
34+
static void Frame_display(const Object* super, RichString* out) {
35+
const Frame* const frame = (const Frame*)super;
36+
if (frame->isError) {
37+
RichString_appendAscii(out, CRT_colors[DEFAULT_COLOR], frame->error);
38+
return;
39+
}
40+
41+
char bufferNumberOfFrame[16] = {'\0'};
42+
int len = snprintf(bufferNumberOfFrame, sizeof(bufferNumberOfFrame), "#%-3d ", frame->index);
43+
RichString_appendnAscii(out, CRT_colors[DYNAMIC_GREEN], bufferNumberOfFrame, len);
44+
45+
char bufferAddress[32] = {'\0'};
46+
len = snprintf(bufferAddress, sizeof(bufferAddress), "0x%016zx ", frame->address);
47+
RichString_appendnAscii(out, CRT_colors[DYNAMIC_BLUE], bufferAddress, len);
48+
49+
RichString_appendAscii(out, CRT_colors[DEFAULT_COLOR], frame->functionName);
50+
if (frame->isSignalFrame) {
51+
RichString_appendAscii(out, CRT_colors[DYNAMIC_RED], " signal frame");
52+
}
53+
54+
char bufferFrameOffset[16] = {'\0'};
55+
len = snprintf(bufferFrameOffset, sizeof(bufferFrameOffset), "+%zu", frame->offset);
56+
RichString_appendAscii(out, CRT_colors[DYNAMIC_YELLOW], bufferFrameOffset);
57+
}
58+
59+
static void BacktracePanel_getFrames(BacktracePanel* this) {
60+
Panel* super = (Panel*) this;
61+
62+
unw_addr_space_t addrSpace = unw_create_addr_space(&_UPT_accessors, 0);
63+
if (!addrSpace) {
64+
xAsprintf(&this->error, "Unable to init libunwind.");
65+
return;
66+
}
67+
68+
const pid_t pid = Process_getPid(this->process);
69+
70+
if (pid == 0) {
71+
xAsprintf(&this->error, "Unable to get the pid");
72+
goto addr_space_error;
73+
}
74+
75+
if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) {
76+
xAsprintf(&this->error, "ptrace: %s", strerror(errno));
77+
goto addr_space_error;
78+
}
79+
wait(NULL);
80+
81+
struct UPT_info* context = _UPT_create(pid);
82+
if (!context) {
83+
xAsprintf(&this->error, "Unable to init backtrace panel.");
84+
goto ptrace_error;
85+
}
86+
87+
unw_cursor_t cursor;
88+
int ret = unw_init_remote(&cursor, addrSpace, context);
89+
if (ret < 0) {
90+
xAsprintf(&this->error, "libunwind cursor: ret=%d", ret);
91+
goto context_error;
92+
}
93+
94+
int index = 0;
95+
do {
96+
char procName[256] = "?";
97+
unw_word_t offset;
98+
unw_word_t pc;
99+
100+
if (unw_get_proc_name(&cursor, procName, sizeof(procName), &offset) == 0) {
101+
ret = unw_get_reg(&cursor, UNW_REG_IP, &pc);
102+
if (ret < 0) {
103+
xAsprintf(&this->error, "unable to get register rip : %d", ret);
104+
break;
105+
}
106+
107+
Frame* frame = Frame_new();
108+
frame->index = index;
109+
frame->address = pc;
110+
frame->offset = offset;
111+
frame->isSignalFrame = unw_is_signal_frame(&cursor);
112+
#if HAVE_LIBIBERTY
113+
char* demangledName = cplus_demangle(procName,
114+
DMGL_PARAMS | DMGL_ANSI | DMGL_VERBOSE | DMGL_RET_POSTFIX);
115+
if (demangledName == NULL) {
116+
xAsprintf(&frame->functionName, "%s", procName);
117+
} else {
118+
xAsprintf(&frame->functionName, "%s", demangledName);
119+
free(demangledName);
120+
}
121+
#else
122+
xAsprintf(&frame->functionName, "%s", procName);
123+
#endif
124+
Panel_add(super, (Object*)frame);
125+
}
126+
index++;
127+
} while (unw_step(&cursor) > 0 && index < MAX_FRAME);
128+
129+
context_error:
130+
_UPT_destroy(context);
131+
132+
ptrace_error:
133+
ptrace(PTRACE_DETACH, pid, 0, 0);
134+
135+
addr_space_error:
136+
unw_destroy_addr_space(addrSpace);
137+
}
138+
139+
BacktracePanel* BacktracePanel_new(const Process* process) {
140+
BacktracePanel* this = CallocThis(BacktracePanel);
141+
this->process = process;
142+
143+
Panel* super = (Panel*) this;
144+
Panel_init(super, 1, 1, 1, 1, Class(Frame), true, FunctionBar_new(BacktraceScreenFunctions, BacktraceScreenKeys, BacktraceScreenEvents));
145+
BacktracePanel_getFrames(this);
146+
if (this->error) {
147+
Panel_prune(super);
148+
149+
Frame* errorFrame = Frame_new();
150+
errorFrame->error = xStrdup(this->error);
151+
errorFrame->isError = true;
152+
Panel_add(super, (Object*)errorFrame);
153+
}
154+
155+
char* header = NULL;
156+
xAsprintf(&header, "Backtrace of '%s' (%d)", process->procComm, Process_getPid(process));
157+
Panel_setHeader(super, header);
158+
free(header);
159+
return this;
160+
}
161+
162+
Frame* Frame_new(void) {
163+
Frame* this = CallocThis(Frame);
164+
return this;
165+
}
166+
167+
static int Frame_compare(const void* object1, const void* object2) {
168+
const Frame* frame1 = (const Frame*)object1;
169+
const Frame* frame2 = (const Frame*)object2;
170+
return String_eq(frame1->functionName, frame2->functionName);
171+
}
172+
173+
static void Frame_delete(Object* object) {
174+
Frame* this = (Frame*)object;
175+
if (this->functionName) {
176+
free(this->functionName);
177+
}
178+
179+
if (this->isError && this->error) {
180+
free(this->error);
181+
}
182+
183+
free(this);
184+
}
185+
186+
void BacktracePanel_delete(Object* object) {
187+
BacktracePanel* this = (BacktracePanel*)object;
188+
if (this->error) {
189+
free(this->error);
190+
}
191+
Panel_delete(object);
192+
}
193+
194+
const PanelClass BacktracePanel_class = {
195+
.super = {
196+
.extends = Class(Panel),
197+
.delete = BacktracePanel_delete,
198+
},
199+
};
200+
201+
const ObjectClass Frame_class = {
202+
.extends = Class(Object),
203+
.compare = Frame_compare,
204+
.delete = Frame_delete,
205+
.display = Frame_display,
206+
};
207+
208+
#endif /* HTOP_LINUX && HAVE_LIBUNWIND_PTRACE */

BacktraceScreen.h

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#ifndef HEADER_BacktraceScreen
2+
#define HEADER_BacktraceScreen
3+
/*
4+
htop - Filename.h
5+
(C) 2021 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 "config.h" // IWYU pragma: keep
11+
12+
#if (defined(HTOP_LINUX) && defined(HAVE_LIBUNWIND_PTRACE))
13+
14+
#include <stddef.h>
15+
16+
#include "Panel.h"
17+
#include "Process.h"
18+
19+
typedef struct BacktracePanel_ {
20+
Panel super;
21+
const Process* process;
22+
char* error;
23+
} BacktracePanel;
24+
25+
typedef struct Frame_ {
26+
Object super;
27+
int index;
28+
size_t address;
29+
size_t offset;
30+
char* functionName;
31+
bool isSignalFrame;
32+
33+
bool isError;
34+
char* error;
35+
} Frame;
36+
37+
BacktracePanel* BacktracePanel_new(const Process* process);
38+
void BacktracePanel_delete(Object* object);
39+
Frame* Frame_new(void);
40+
41+
extern const PanelClass BacktracePanel_class;
42+
extern const ObjectClass Frame_class;
43+
44+
#endif /* HTOP_LINUX && HAVE_LIBUNWIND_PTRACE */
45+
46+
#endif

Makefile.am

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ myhtopsources = \
3333
AffinityPanel.c \
3434
AvailableColumnsPanel.c \
3535
AvailableMetersPanel.c \
36+
BacktraceScreen.c \
3637
BatteryMeter.c \
3738
CategoriesPanel.c \
3839
ClockMeter.c \
@@ -99,6 +100,7 @@ myhtopheaders = \
99100
AffinityPanel.h \
100101
AvailableColumnsPanel.h \
101102
AvailableMetersPanel.h \
103+
BacktraceScreen.h \
102104
BatteryMeter.h \
103105
CPUMeter.h \
104106
CRT.h \

Object.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ typedef void(*Object_Delete)(Object*);
3333
#define Class(class_) ((const ObjectClass*)(&(class_ ## _class)))
3434

3535
#define AllocThis(class_) (class_*) xMalloc(sizeof(class_)); Object_setClass(this, Class(class_))
36+
#define CallocThis(class_) (class_*) xCalloc(sizeof(class_), 1); Object_setClass(this, Class(class_))
3637

3738
typedef struct ObjectClass_ {
3839
const void* const extends;

README

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,12 @@ To install on the local system run `make install`. By default `make install` ins
139139
enable Linux delay accounting support
140140
- dependencies: *pkg-config*(build-time), *libnl-3* and *libnl-genl-3*
141141
- default: *check*
142+
* `--enable-unwind-ptrace`:
143+
enable backtrace support
144+
- default: *no*
145+
* `--enable-libiberty`:
146+
enable the demangling support for the backtraces
147+
- default: *no*
142148

143149

144150
## Runtime dependencies:

0 commit comments

Comments
 (0)