Skip to content

Commit 792bdba

Browse files
committed
Add backtrace screen
1 parent 2cc2f73 commit 792bdba

5 files changed

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

0 commit comments

Comments
 (0)