Skip to content

Commit

Permalink
Add backtrace screen
Browse files Browse the repository at this point in the history
  • Loading branch information
MrCirdo committed Mar 16, 2024
1 parent 66d43d3 commit cfe5300
Show file tree
Hide file tree
Showing 6 changed files with 292 additions and 0 deletions.
29 changes: 29 additions & 0 deletions Action.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ in the source distribution for its full text.
#include <stdlib.h>
#include <string.h>

#include "BacktraceScreen.h"
#include "CRT.h"
#include "CategoriesPanel.h"
#include "CommandScreen.h"
Expand Down Expand Up @@ -595,6 +596,27 @@ static Htop_Reaction actionShowLocks(State* st) {
return HTOP_REFRESH | HTOP_REDRAW_BAR;
}

#if (defined(HTOP_LINUX) && defined(HAVE_LIBUNWIND_PTRACE))
static Htop_Reaction actionBacktrace(State *st) {
const Process* process = (Process*) Panel_getSelected((Panel*)st->mainPanel);
if (!process)
return HTOP_OK;

BacktracePanel *panel = BacktracePanel_new(process);
ScreenManager *screenManager = ScreenManager_new(NULL, st->host, st, false);
ScreenManager_add(screenManager, (Panel *)panel, 0);

Panel *lastFocusPanel = NULL;
int lastKey = 0;

ScreenManager_run(screenManager, &lastFocusPanel, &lastKey, NULL);
BacktracePanel_delete((Object *)panel);
ScreenManager_delete(screenManager);

return HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
}
#endif

static Htop_Reaction actionStrace(State* st) {
if (!Action_writeableProcess(st))
return HTOP_OK;
Expand Down Expand Up @@ -679,6 +701,10 @@ static const struct {
#if (defined(HAVE_LIBHWLOC) || defined(HAVE_AFFINITY))
{ .key = " a: ", .roInactive = true, .info = "set CPU affinity" },
#endif

#if (defined(HTOP_LINUX) && defined(HAVE_LIBUNWIND_PTRACE))
{ .key = " b: ", .roInactive = false, .info = "show the backtrace of user process" },
#endif
{ .key = " e: ", .roInactive = false, .info = "show process environment" },
{ .key = " i: ", .roInactive = true, .info = "set IO priority" },
{ .key = " l: ", .roInactive = true, .info = "list open files with lsof" },
Expand Down Expand Up @@ -918,6 +944,9 @@ void Action_setBindings(Htop_Action* keys) {
keys['\\'] = actionIncFilter;
keys[']'] = actionHigherPriority;
keys['a'] = actionSetAffinity;
#if (defined(HTOP_LINUX) && defined(HAVE_LIBUNWIND_PTRACE))
keys['b'] = actionBacktrace;
#endif
keys['c'] = actionTagAllChildren;
keys['e'] = actionShowEnvScreen;
keys['h'] = actionHelp;
Expand Down
208 changes: 208 additions & 0 deletions BacktraceScreen.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
/*
htop - Action.c
(C) 2015 Hisham H. Muhammad
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/

#include "BacktraceScreen.h"

#if (defined(HTOP_LINUX) && defined(HAVE_LIBUNWIND_PTRACE))

#include <sys/wait.h>

#include "CRT.h"
#include "Object.h"
#include "Panel.h"
#include "Process.h"
#include "RichString.h"
#include "XUtils.h"
#include "errno.h"

#include <libiberty/demangle.h>
#include <libunwind-ptrace.h>
#include <sys/ptrace.h>

#define MAX_FRAME 256

static const char* const BacktraceScreenFunctions[] = {"Done ", NULL};

static const char* const BacktraceScreenKeys[] = {"Esc"};

static const int BacktraceScreenEvents[] = {27};

static void Frame_display(const Object* super, RichString* out) {
const Frame* const frame = (const Frame*)super;
if (frame->isError) {
RichString_appendAscii(out, CRT_colors[DEFAULT_COLOR], frame->error);
return;
}

char bufferNumberOfFrame[16] = {'\0'};
int len = snprintf(bufferNumberOfFrame, sizeof(bufferNumberOfFrame), "#%-3d ", frame->index);
RichString_appendnAscii(out, CRT_colors[DYNAMIC_GREEN], bufferNumberOfFrame, len);

char bufferAddress[32] = {'\0'};
len = snprintf(bufferAddress, sizeof(bufferAddress), "0x%016zx ", frame->address);
RichString_appendnAscii(out, CRT_colors[DYNAMIC_BLUE], bufferAddress, len);

RichString_appendAscii(out, CRT_colors[DEFAULT_COLOR], frame->functionName);
if (frame->isSignalFrame) {
RichString_appendAscii(out, CRT_colors[DYNAMIC_RED], " signal frame");
}

char bufferFrameOffset[16] = {'\0'};
len = snprintf(bufferFrameOffset, sizeof(bufferFrameOffset), "+%zu", frame->offset);
RichString_appendAscii(out, CRT_colors[DYNAMIC_YELLOW], bufferFrameOffset);
}

static void BacktracePanel_getFrames(BacktracePanel* this) {
Panel* super = (Panel*) this;

unw_addr_space_t addrSpace = unw_create_addr_space(&_UPT_accessors, 0);
if (!addrSpace) {
xAsprintf(&this->error, "Unable to init libunwind.");
return;
}

const pid_t pid = Process_getPid(this->process);

if (pid == 0) {
xAsprintf(&this->error, "Unable to get the pid");
goto addr_space_error;
}

if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) {
xAsprintf(&this->error, "ptrace: %s", strerror(errno));
goto addr_space_error;
}
wait(NULL);

struct UPT_info* context = _UPT_create(pid);
if (!context) {
xAsprintf(&this->error, "Unable to init backtrace panel.");
goto ptrace_error;
}

unw_cursor_t cursor;
int ret = unw_init_remote(&cursor, addrSpace, context);
if (ret < 0) {
xAsprintf(&this->error, "libunwind cursor: ret=%d", ret);
goto context_error;
}

int index = 0;
do {
char procName[256] = "?";
unw_word_t offset;
unw_word_t pc;

if (unw_get_proc_name(&cursor, procName, sizeof(procName), &offset) == 0) {
ret = unw_get_reg(&cursor, UNW_REG_IP, &pc);
if (ret < 0) {
xAsprintf(&this->error, "unable to get register rip : %d", ret);
break;
}

Frame* frame = Frame_new();
frame->index = index;
frame->address = pc;
frame->offset = offset;
frame->isSignalFrame = unw_is_signal_frame(&cursor);
#if HAVE_LIBIBERTY
char* demangledName = cplus_demangle(procName,
DMGL_PARAMS | DMGL_ANSI | DMGL_VERBOSE | DMGL_RET_POSTFIX);
if (demangledName == NULL) {
xAsprintf(&frame->functionName, "%s", procName);
} else {
xAsprintf(&frame->functionName, "%s", demangledName);
free(demangledName);
}
#else
xAsprintf(&frame->functionName, "%s", procName);
#endif
Panel_add(super, (Object*)frame);
}
index++;
} while (unw_step(&cursor) > 0 && index < MAX_FRAME);

context_error:
_UPT_destroy(context);

ptrace_error:
ptrace(PTRACE_DETACH, pid, 0, 0);

addr_space_error:
unw_destroy_addr_space(addrSpace);
}

BacktracePanel* BacktracePanel_new(const Process* process) {
BacktracePanel* this = CallocThis(BacktracePanel);
this->process = process;

Panel* super = (Panel*) this;
Panel_init(super, 1, 1, 1, 1, Class(Frame), true, FunctionBar_new(BacktraceScreenFunctions, BacktraceScreenKeys, BacktraceScreenEvents));
BacktracePanel_getFrames(this);
if (this->error) {
Panel_prune(super);

Frame* errorFrame = Frame_new();
errorFrame->error = xStrdup(this->error);
errorFrame->isError = true;
Panel_add(super, (Object*)errorFrame);
}

char* header = NULL;
xAsprintf(&header, "Backtrace of '%s' (%d)", process->procComm, Process_getPid(process));
Panel_setHeader(super, header);
free(header);
return this;
}

Frame* Frame_new(void) {
Frame* this = CallocThis(Frame);
return this;
}

static int Frame_compare(const void* object1, const void* object2) {
const Frame* frame1 = (const Frame*)object1;
const Frame* frame2 = (const Frame*)object2;
return String_eq(frame1->functionName, frame2->functionName);
}

static void Frame_delete(Object* object) {
Frame* this = (Frame*)object;
if (this->functionName) {
free(this->functionName);
}

if (this->isError && this->error) {
free(this->error);
}

free(this);
}

void BacktracePanel_delete(Object* object) {
BacktracePanel* this = (BacktracePanel*)object;
if (this->error) {
free(this->error);
}
Panel_delete(object);
}

const PanelClass BacktracePanel_class = {
.super = {
.extends = Class(Panel),
.delete = BacktracePanel_delete,
},
};

const ObjectClass Frame_class = {
.extends = Class(Object),
.compare = Frame_compare,
.delete = Frame_delete,
.display = Frame_display,
};

#endif /* HTOP_LINUX && HAVE_LIBUNWIND_PTRACE */
46 changes: 46 additions & 0 deletions BacktraceScreen.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#ifndef HEADER_BacktraceScreen
#define HEADER_BacktraceScreen
/*
htop - Filename.h
(C) 2021 htop dev team
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/

#include "config.h" // IWYU pragma: keep

#if (defined(HTOP_LINUX) && defined(HAVE_LIBUNWIND_PTRACE))

#include <stddef.h>

#include "Panel.h"
#include "Process.h"

typedef struct BacktracePanel_ {
Panel super;
const Process* process;
char* error;
} BacktracePanel;

typedef struct Frame_ {
Object super;
int index;
size_t address;
size_t offset;
char* functionName;
bool isSignalFrame;

bool isError;
char* error;
} Frame;

BacktracePanel* BacktracePanel_new(const Process* process);
void BacktracePanel_delete(Object* object);
Frame* Frame_new(void);

extern const PanelClass BacktracePanel_class;
extern const ObjectClass Frame_class;

#endif /* HTOP_LINUX && HAVE_LIBUNWIND_PTRACE */

#endif
2 changes: 2 additions & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ myhtopsources = \
AffinityPanel.c \
AvailableColumnsPanel.c \
AvailableMetersPanel.c \
BacktraceScreen.c \
BatteryMeter.c \
CategoriesPanel.c \
ClockMeter.c \
Expand Down Expand Up @@ -99,6 +100,7 @@ myhtopheaders = \
AffinityPanel.h \
AvailableColumnsPanel.h \
AvailableMetersPanel.h \
BacktraceScreen.h \
BatteryMeter.h \
CPUMeter.h \
CRT.h \
Expand Down
1 change: 1 addition & 0 deletions Object.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ typedef void(*Object_Delete)(Object*);
#define Class(class_) ((const ObjectClass*)(&(class_ ## _class)))

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

typedef struct ObjectClass_ {
const void* const extends;
Expand Down
6 changes: 6 additions & 0 deletions README
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,12 @@ To install on the local system run `make install`. By default `make install` ins
enable Linux delay accounting support
- dependencies: *pkg-config*(build-time), *libnl-3* and *libnl-genl-3*
- default: *check*
* `--enable-unwind-ptrace`:
enable backtrace support
- default: *no*
* `--enable-libiberty`:
enable the demangling support for the backtraces
- default: *no*


## Runtime dependencies:
Expand Down

0 comments on commit cfe5300

Please sign in to comment.