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 Apr 10, 2024
1 parent 5393ce6 commit 33153c4
Show file tree
Hide file tree
Showing 24 changed files with 468 additions and 2 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 the backtrace of user process" },
#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
1 change: 1 addition & 0 deletions Action.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ htop - Action.h
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
#include "config.h" // IWYU pragma: keep

#include <stdbool.h>
#include <sys/types.h>
Expand Down
206 changes: 206 additions & 0 deletions BacktraceScreen.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
/*
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,
DEMANGLE_NAME_FUNCTION = 1 << 0,
} BacktraceScreenDisplayOptions;

static BacktraceScreenDisplayOptions displayOptions = DEMANGLE_NAME_FUNCTION;

static const char* const BacktraceScreenFunctions[] = {
"Refresh ",
"Show demangled function name",
"Done ",
NULL
};

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

static const int BacktraceScreenEvents[] = {
KEY_F(1),
KEY_F(2),
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 ((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);
}

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 (this->demangleFunctionName) {
free(this->demangleFunctionName);
}

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 = CallocThis(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 = CallocThis(BacktraceFrame);
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;

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;
}
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
45 changes: 45 additions & 0 deletions BacktraceScreen.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#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 "config.h" // IWYU pragma: keep

#if defined(HAVE_BACKTRACE)
#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
#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 darwin/Platform.c
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,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
2 changes: 2 additions & 0 deletions dragonflybsd/Platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,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
Loading

0 comments on commit 33153c4

Please sign in to comment.