Skip to content

Commit

Permalink
Add backtrace screen
Browse files Browse the repository at this point in the history
Co-authored-by: Benny Baumann <[email protected]>
Co-authored-by: Kang-Che Sung <[email protected]>
  • Loading branch information
3 people committed Jun 16, 2024
1 parent e36fb9f commit fe1f936
Show file tree
Hide file tree
Showing 23 changed files with 491 additions and 0 deletions.
30 changes: 30 additions & 0 deletions Action.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ in the source distribution for its full text.
#include "ListItem.h"
#include "Macros.h"
#include "MainPanel.h"
#include "Object.h"
#include "OpenFilesScreen.h"
#include "Panel.h"
#include "Process.h"
#include "ProcessLocksScreen.h"
#include "ProvideCurses.h"
Expand All @@ -47,6 +49,10 @@ in the source distribution for its full text.
#include "AffinityPanel.h"
#endif

#if defined(HAVE_BACKTRACE)
#include "BacktraceScreen.h"
#endif


Object* Action_pickFromVector(State* st, Panel* list, int x, bool follow) {
MainPanel* mainPanel = st->mainPanel;
Expand Down Expand Up @@ -595,6 +601,24 @@ static Htop_Reaction actionShowLocks(State* st) {
return HTOP_REFRESH | HTOP_REDRAW_BAR;
}

#if defined(HAVE_BACKTRACE)
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);

ScreenManager_run(screenManager, NULL, NULL, 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 @@ -678,6 +702,9 @@ static const struct {
{ .key = " F8 [: ", .roInactive = true, .info = "lower priority (+ nice)" },
#if (defined(HAVE_LIBHWLOC) || defined(HAVE_AFFINITY))
{ .key = " a: ", .roInactive = true, .info = "set CPU affinity" },
#endif
#if defined(HAVE_BACKTRACE)
{ .key = " b: ", .roInactive = false, .info = "show process backtrace" },
#endif
{ .key = " e: ", .roInactive = false, .info = "show process environment" },
{ .key = " i: ", .roInactive = true, .info = "set IO priority" },
Expand Down Expand Up @@ -918,6 +945,9 @@ void Action_setBindings(Htop_Action* keys) {
keys['\\'] = actionIncFilter;
keys[']'] = actionHigherPriority;
keys['a'] = actionSetAffinity;
#if defined(HAVE_BACKTRACE)
keys['b'] = actionBacktrace;
#endif
keys['c'] = actionTagAllChildren;
keys['e'] = actionShowEnvScreen;
keys['h'] = actionHelp;
Expand Down
233 changes: 233 additions & 0 deletions BacktraceScreen.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
/*
htop - BacktraceScreen.h
(C) 2023 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

#include "BacktraceScreen.h"

#if defined(HAVE_BACKTRACE)

#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>

#include <ncursesw/curses.h>

#include "CRT.h"
#include "FunctionBar.h"
#include "ListItem.h"
#include "Object.h"
#include "Panel.h"
#include "Platform.h"
#include "Process.h"
#include "RichString.h"
#include "Vector.h"
#include "XUtils.h"

typedef enum BacktraceScreenDisplayOptions_ {
NO_OPTION = 0,
#if defined(HAVE_DEMANGLING)
DEMANGLE_NAME_FUNCTION = 1 << 0,
#endif
} BacktraceScreenDisplayOptions;

#if defined(HAVE_DEMANGLING)
static BacktraceScreenDisplayOptions displayOptions = DEMANGLE_NAME_FUNCTION;
#else
static BacktraceScreenDisplayOptions displayOptions = NO_OPTION;
#endif

static const char* const BacktraceScreenFunctions[] = {
"Refresh ",
#if defined(HAVE_DEMANGLING)
"Show demangled function name",
#endif
"Done ",
NULL
};

static const char* const BacktraceScreenKeys[] = {
"F1",
#if defined(HAVE_DEMANGLING)
"F2",
#endif
"Esc"
};

static const int BacktraceScreenEvents[] = {
KEY_F(1),
#if defined(HAVE_DEMANGLING)
KEY_F(2),
#endif
27,
};

static void Frame_display(const Object* super, RichString* out) {
const BacktraceFrame* const frame = (const BacktraceFrame* const)super;

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);

#if defined(HAVE_DEMANGLING)
if ((displayOptions& DEMANGLE_NAME_FUNCTION) == DEMANGLE_NAME_FUNCTION &&
frame->demangleFunctionName) {
RichString_appendAscii(out, CRT_colors[DEFAULT_COLOR], frame->demangleFunctionName);
} else {
RichString_appendAscii(out, CRT_colors[DEFAULT_COLOR], frame->functionName);
}
#else
RichString_appendAscii(out, CRT_colors[DEFAULT_COLOR], frame->functionName);
#endif

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

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

if (frame->objectPath) {
RichString_appendAscii(out, CRT_colors[DYNAMIC_CYAN], frame->objectPath);
}
}

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

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

#if defined(HAVE_DEMANGLING)
if (this->demangleFunctionName) {
free(this->demangleFunctionName);
}
#endif

if (this->objectPath) {
free(this->objectPath);
}

free(this);
}

static void BacktracePanel_setError(BacktracePanel* this, char* error) {
assert(error != NULL);
assert(this->error == false);

Panel* super = &this->super;
Panel_prune(super);

Vector* lines = this->super.items;
Vector_delete(lines);
this->super.items = Vector_new(Class(ListItem), true, DEFAULT_SIZE);
Panel_set(super, 0, (Object*)ListItem_new(error, 0));
}

static void BacktracePanel_populateFrames(BacktracePanel* this) {
if (this->error) {
return;
}

char* error = NULL;
Vector* lines = this->super.items;
Platform_getBacktrace(lines, this->process, &error);
if (error) {
BacktracePanel_setError(this, error);
free(error);
}

this->super.needsRedraw = true;
}

BacktracePanel* BacktracePanel_new(const Process* process) {
BacktracePanel* this = AllocThis(BacktracePanel);
this->process = process;
this->error = false;

Panel* super = (Panel*) this;
Panel_init(super, 1, 1, 1, 1, Class(BacktraceFrame), true, FunctionBar_new(BacktraceScreenFunctions, BacktraceScreenKeys, BacktraceScreenEvents));

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

BacktracePanel_populateFrames(this);
return this;
}

BacktraceFrame* BacktraceFrame_new(void) {
BacktraceFrame* this = AllocThis(BacktraceFrame);
this->index = 0;;
this->address = 0;
this->offset = 0;
this->functionName = NULL;
this->demangleFunctionName = NULL;
this->objectPath = NULL;
this->isSignalFrame = false;
return this;
}

static HandlerResult BacktracePanel_eventHandler(Panel* super, int ch) {
BacktracePanel* this = (BacktracePanel*)super;

HandlerResult result = IGNORED;
switch (ch) {
case KEY_F(1):
Panel_prune(super);
BacktracePanel_populateFrames(this);
break;

#if defined(HAVE_DEMANGLING)
case KEY_F(2):
if ((displayOptions& DEMANGLE_NAME_FUNCTION) == DEMANGLE_NAME_FUNCTION) {
displayOptions &= ~DEMANGLE_NAME_FUNCTION;
FunctionBar_setLabel(super->defaultBar, KEY_F(2), "Show mangled function name");
} else {
displayOptions |= DEMANGLE_NAME_FUNCTION;
FunctionBar_setLabel(super->defaultBar, KEY_F(2), "Show demangled function name");
}
this->super.needsRedraw = true;
break;
#endif
}
return result;
}

void BacktracePanel_delete(Object* object) {
Panel_delete(object);
}

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

const ObjectClass BacktraceFrame_class = {
.extends = Class(Object),
.compare = Frame_compare,
.delete = Frame_delete,
.display = Frame_display,
};
#endif
42 changes: 42 additions & 0 deletions BacktraceScreen.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#ifndef HEADER_BacktraceScreen
#define HEADER_BacktraceScreen
/*
htop - BacktraceScreen.h
(C) 2023 htop dev team
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/

#include <stddef.h>
#include <stdbool.h>

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

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

typedef struct BacktraceFrame_ {
Object super;
int index;
size_t address;
size_t offset;
char* functionName;
char* demangleFunctionName;
char* objectPath;
bool isSignalFrame;
} BacktraceFrame;

BacktracePanel* BacktracePanel_new(const Process* process);
void BacktracePanel_delete(Object* object);
BacktraceFrame* BacktraceFrame_new(void);

extern const PanelClass BacktracePanel_class;
extern const ObjectClass BacktraceFrame_class;

#endif

4 changes: 4 additions & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,10 @@ myhtopheaders = \
Vector.h \
XUtils.h

if HAVE_BACKTRACE
myhtopheaders += BacktraceScreen.h
myhtopsources += BacktraceScreen.c
endif
# Linux
# -----

Expand Down
1 change: 1 addition & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,7 @@ esac
if test "x$enable_backtrace" != xno; then
AC_DEFINE([HAVE_BACKTRACE], [1], [Define if the backtrace feature is enabled])
fi
AM_CONDITIONAL([HAVE_BACKTRACE], [test x"$enable_backtrace" != xno])

if test "x$enable_backtrace" = xunwind-ptrace; then
AC_MSG_CHECKING([if libunwind has unw_get_elf_filename])
Expand Down
6 changes: 6 additions & 0 deletions darwin/Platform.c
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,12 @@ bool Platform_getNetworkIO(NetworkIOData* data) {
return true;
}

void Platform_getBacktrace(Vector* frames, const Process* process, char **error) {
(void)frames;
(void)process;
xAsprintf(error, "The backtrace screen is not implemented");
}

void Platform_getBattery(double* percent, ACPresence* isOnAC) {
*percent = NAN;
*isOnAC = AC_ERROR;
Expand Down
2 changes: 2 additions & 0 deletions darwin/Platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ bool Platform_getDiskIO(DiskIOData* data);

bool Platform_getNetworkIO(NetworkIOData* data);

void Platform_getBacktrace(Vector* frames, const Process* process, char **error);

void Platform_getBattery(double* percent, ACPresence* isOnAC);

static inline void Platform_getHostname(char* buffer, size_t size) {
Expand Down
6 changes: 6 additions & 0 deletions dragonflybsd/Platform.c
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,12 @@ bool Platform_getNetworkIO(NetworkIOData* data) {
return true;
}

void Platform_getBacktrace(Vector* frames, const Process* process, char **error) {
(void)frames;
(void)process;
xAsprintf(error, "The backtrace screen is not implemented");
}

void Platform_getBattery(double* percent, ACPresence* isOnAC) {
int life;
size_t life_len = sizeof(life);
Expand Down
Loading

0 comments on commit fe1f936

Please sign in to comment.