Skip to content

Commit 6ec443b

Browse files
committed
Add Backtrace Screen
Signed-off-by: Odric Roux-Paris <odricrouxparis@gmail.com>
1 parent 05b7ae8 commit 6ec443b

4 files changed

Lines changed: 243 additions & 0 deletions

File tree

Action.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ in the source distribution for its full text.
1313
#include <stdbool.h>
1414
#include <stdlib.h>
1515

16+
#include "BacktraceScreen.h"
1617
#include "CRT.h"
1718
#include "CategoriesPanel.h"
1819
#include "CommandScreen.h"
@@ -582,6 +583,20 @@ static Htop_Reaction actionShowLocks(State* st) {
582583
return HTOP_REFRESH | HTOP_REDRAW_BAR;
583584
}
584585

586+
static Htop_Reaction actionBacktrace(State *st) {
587+
const Process* process = (Process*) Panel_getSelected((Panel*)st->mainPanel);
588+
if (!process)
589+
return HTOP_OK;
590+
591+
BacktraceScreen *backtrace_screen = BacktraceScreen_new(process);
592+
InfoScreen_run((InfoScreen *) backtrace_screen);
593+
594+
BacktraceScreen_delete((Object *)backtrace_screen);
595+
clear();
596+
CRT_enableDelay();
597+
return HTOP_REFRESH | HTOP_REDRAW_BAR;
598+
}
599+
585600
static Htop_Reaction actionStrace(State* st) {
586601
if (!Action_writeableProcess(st))
587602
return HTOP_OK;
@@ -665,6 +680,7 @@ static const struct {
665680
#if (defined(HAVE_LIBHWLOC) || defined(HAVE_AFFINITY))
666681
{ .key = " a: ", .roInactive = true, .info = "set CPU affinity" },
667682
#endif
683+
{ .key = " b: ", .roInactive = false, .info = "show the backtrace of user process" },
668684
{ .key = " e: ", .roInactive = false, .info = "show process environment" },
669685
{ .key = " i: ", .roInactive = true, .info = "set IO priority" },
670686
{ .key = " l: ", .roInactive = true, .info = "list open files with lsof" },
@@ -904,6 +920,7 @@ void Action_setBindings(Htop_Action* keys) {
904920
keys['\\'] = actionIncFilter;
905921
keys[']'] = actionHigherPriority;
906922
keys['a'] = actionSetAffinity;
923+
keys['b'] = actionBacktrace;
907924
keys['c'] = actionTagAllChildren;
908925
keys['e'] = actionShowEnvScreen;
909926
keys['h'] = actionHelp;

BacktraceScreen.c

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
#include "BacktraceScreen.h"
2+
3+
#include <ctype.h>
4+
#include <execinfo.h>
5+
#include <stdio.h>
6+
#include <string.h>
7+
#include <sys/ptrace.h>
8+
#include <sys/types.h>
9+
#include <sys/wait.h>
10+
#include <unistd.h>
11+
12+
#include <libunwind-ptrace.h>
13+
14+
#include "FunctionBar.h"
15+
#include "InfoScreen.h"
16+
#include "Object.h"
17+
#include "Panel.h"
18+
#include "Process.h"
19+
#include "Vector.h"
20+
#include "XUtils.h"
21+
22+
static const char* const BackTraceScreenFunctions[] = {"Refresh", "Done", NULL};
23+
static const char* const BackTraceScreenKeys[] = {"F3", "Esc"};
24+
static const int BackTraceScreenEvents[] = {KEY_F(3), 27};
25+
26+
static void BacktraceScreen_getMaxWidth(size_t* maxFunctionWidth, size_t* maxPathWidth, Backtrace* backtraces, size_t maxBacktraces) {
27+
*maxFunctionWidth = 0;
28+
*maxPathWidth = 0;
29+
30+
for (size_t i = 0; i < maxBacktraces; i++) {
31+
Backtrace* backtrace = &backtraces[i];
32+
for (size_t j = 0; j < backtrace->nbFrames; j++) {
33+
Frame* frame = &backtrace->frames[j];
34+
size_t lenFunction = strlen(frame->function);
35+
36+
if (lenFunction > *maxFunctionWidth)
37+
*maxFunctionWidth = lenFunction;
38+
39+
size_t lenPath = strlen(frame->path);
40+
if (lenPath > *maxPathWidth)
41+
*maxPathWidth = lenPath;
42+
}
43+
}
44+
}
45+
46+
static void BacktraceScreen_freeFrame(Frame* frame) {
47+
free(frame->function);
48+
free(frame->path);
49+
}
50+
51+
static void BacktraceScreen_freeBacktraces(Backtrace* backtraces) {
52+
if (!backtraces)
53+
return;
54+
55+
Backtrace* backtraceArray = backtraces;
56+
for (size_t backtraceIndex = 0; backtraceIndex < MAX_BACKTRACE; backtraceIndex++) {
57+
Backtrace* currentBacktrace = &backtraces[backtraceIndex];
58+
for (size_t frameIndex = 0; frameIndex < currentBacktrace->nbFrames; frameIndex++) {
59+
Frame* currentFrame = &currentBacktrace->frames[frameIndex];
60+
BacktraceScreen_freeFrame(currentFrame);
61+
}
62+
}
63+
free(backtraceArray);
64+
}
65+
66+
BacktraceScreen* BacktraceScreen_new(const Process* process) {
67+
BacktraceScreen* this = xCalloc(1, sizeof(BacktraceScreen));
68+
Object_setClass(this, Class(BacktraceScreen));
69+
FunctionBar* functionBar = FunctionBar_new(BackTraceScreenFunctions, BackTraceScreenKeys, BackTraceScreenEvents);
70+
CRT_disableDelay();
71+
return (BacktraceScreen*) InfoScreen_init(&this->super, process, functionBar, LINES - 2, "NB ADDRESS NAME PATH ");
72+
}
73+
74+
void BacktraceScreen_delete(Object* cast) {
75+
BacktraceScreen* this = (BacktraceScreen*) cast;
76+
BacktraceScreen_freeBacktraces(this->backtraces);
77+
CRT_enableDelay();
78+
free(InfoScreen_done((InfoScreen*)this));
79+
}
80+
81+
static void BacktraceScreen_draw(InfoScreen* this) {
82+
if (Process_isThread(this->process))
83+
InfoScreen_drawTitled(this, "Backtrace of thread %d - %s", this->process->pid, Process_getCommand(this->process));
84+
else
85+
InfoScreen_drawTitled(this, "Backtrace of process %d - %s", this->process->pid, Process_getCommand(this->process));
86+
}
87+
88+
static void BacktraceScreen_print(BacktraceScreen* backtraceScreen) {
89+
const Process* process = backtraceScreen->super.process;
90+
bool isThread = Process_isThread(process);
91+
92+
size_t maxFunctionWidth = 0;
93+
size_t maxPathWidth = 0;
94+
BacktraceScreen_getMaxWidth(&maxFunctionWidth, &maxPathWidth, backtraceScreen->backtraces, backtraceScreen->nbBacktraces);
95+
96+
char* header = NULL;
97+
xAsprintf(&header, " NB ADDRESS NAME %*s PATH", (int)(maxFunctionWidth - sizeof("NAME")), " ");
98+
Panel_setHeader(backtraceScreen->super.display, header);
99+
100+
free(header);
101+
102+
for (size_t i = 0; i < backtraceScreen->nbBacktraces; i++) {
103+
Backtrace* backtrace = &backtraceScreen->backtraces[i];
104+
char* tgidStr = NULL;
105+
if (!isThread) {
106+
xAsprintf(&tgidStr, "TID: %d", backtrace->tgid);
107+
InfoScreen_addLine(&backtraceScreen->super, tgidStr);
108+
}
109+
110+
if (isThread && backtrace->tgid != process->tgid) {
111+
continue;
112+
}
113+
114+
for (size_t j = 0; j < backtrace->nbFrames; j++) {
115+
Frame* frame = &backtrace->frames[j];
116+
117+
char* frameStr = NULL;
118+
xAsprintf(&frameStr, " #%zu 0x%016zx %-*s %s", frame->nb, frame->address, (int)maxFunctionWidth, frame->function, frame->path);
119+
InfoScreen_addLine(&backtraceScreen->super, frameStr);
120+
free(frameStr);
121+
}
122+
free(tgidStr);
123+
}
124+
}
125+
126+
static void BacktraceScreen_getBacktraceFromProcess(const Process *process, Backtrace *backtraces, size_t maxBacktraces) {
127+
pid_t pid = Process_isThread(process) ? process->tgid : process->pid;
128+
129+
unw_addr_space_t addrSpace = unw_create_addr_space(&_UPT_accessors, 0);
130+
ptrace(PTRACE_ATTACH, pid, 0, 0);
131+
void *context = _UPT_create(pid);
132+
unw_cursor_t cursor;
133+
unw_init_remote(&cursor, addrSpace, context);
134+
135+
do {
136+
unw_word_t offset = 0;
137+
138+
char symbolName[4096];
139+
if (unw_get_proc_name(&cursor, symbolName, sizeof(symbolName), &offset) == 0) {
140+
141+
}
142+
143+
} while (unw_step(&cursor));
144+
145+
146+
_UPT_destroy(context);
147+
unw_destroy_addr_space(addrSpace);
148+
ptrace(PTRACE_DETACH, pid);
149+
}
150+
151+
static void BacktraceScreen_scan(InfoScreen* super) {
152+
BacktraceScreen* this = (BacktraceScreen*)super;
153+
this->backtraces = xCalloc(MAX_BACKTRACE, sizeof(Backtrace));
154+
155+
BacktraceScreen_print(this);
156+
}
157+
158+
static bool BacktraceScreen_onKey(InfoScreen* super, int character) {
159+
BacktraceScreen* this = (BacktraceScreen*)super;
160+
switch (character) {
161+
case 'r':
162+
case KEY_F(3):
163+
BacktraceScreen_freeBacktraces(this->backtraces);
164+
Panel_prune(super->display);
165+
Vector_prune(super->lines);
166+
BacktraceScreen_scan(super);
167+
return true;
168+
}
169+
return false;
170+
}
171+
172+
const InfoScreenClass BacktraceScreen_class = {
173+
.super = {
174+
.extends = Class(Object),
175+
.delete = BacktraceScreen_delete,
176+
},
177+
.scan = BacktraceScreen_scan,
178+
.draw = BacktraceScreen_draw,
179+
.onKey = BacktraceScreen_onKey,
180+
};

BacktraceScreen.h

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#ifndef HEADER_BacktraceScreen
2+
#define HEADER_BacktraceScreen
3+
/*
4+
htop - BacktraceSceen.h
5+
(C) 2023 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 <stddef.h>
11+
12+
#include "InfoScreen.h"
13+
#include "Object.h"
14+
#include "Process.h"
15+
16+
#define MAX_FRAME 256
17+
#define MAX_BACKTRACE 1024
18+
19+
typedef struct Frame_ {
20+
size_t nb;
21+
size_t address;
22+
char* function;
23+
char* path;
24+
} Frame;
25+
26+
typedef struct Backtrace_ {
27+
pid_t pid;
28+
pid_t tgid;
29+
Frame frames[MAX_FRAME];
30+
size_t nbFrames;
31+
} Backtrace;
32+
33+
typedef struct BacktraceScreen_ {
34+
InfoScreen super;
35+
Backtrace* backtraces;
36+
size_t nbBacktraces;
37+
} BacktraceScreen;
38+
39+
extern const InfoScreenClass BacktraceScreen_class;
40+
41+
BacktraceScreen* BacktraceScreen_new(const Process* process);
42+
void BacktraceScreen_delete(Object* cast);
43+
44+
#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 \

0 commit comments

Comments
 (0)