diff --git a/BlackMagicProbe.pdf b/BlackMagicProbe.pdf index eef4659..8303af1 100644 Binary files a/BlackMagicProbe.pdf and b/BlackMagicProbe.pdf differ diff --git a/doc/bmdebug.png b/doc/bmdebug.png index 4f0e105..672ca3b 100644 Binary files a/doc/bmdebug.png and b/doc/bmdebug.png differ diff --git a/doc/bmflash.png b/doc/bmflash.png index b70142a..112856e 100644 Binary files a/doc/bmflash.png and b/doc/bmflash.png differ diff --git a/doc/bmprofile-top.png b/doc/bmprofile-top.png index 0b23df8..6174a11 100644 Binary files a/doc/bmprofile-top.png and b/doc/bmprofile-top.png differ diff --git a/doc/bmtrace.png b/doc/bmtrace.png index 16cdab9..0cd23de 100644 Binary files a/doc/bmtrace.png and b/doc/bmtrace.png differ diff --git a/examples/function_trace.c b/examples/function_trace.c new file mode 100644 index 0000000..54e35a3 --- /dev/null +++ b/examples/function_trace.c @@ -0,0 +1,76 @@ +/* Example functions for function enter/exit tracing via GCC instrumentation. + * It is based on SWO tracing, and should be used with the function_trace.tsdl + * file. + * + * Routines for initializing the micro-controller for TRACESWO are not included, + * as these are (in part) dependend on the particular micro-controller. + * + * + * Copyright 2022 CompuPhase + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#include + +#if !defined ITM_TCR_ITMENA + #define ITM_TCR_ITMENA ITM_TCR_ITMENA_Msk +#endif + +__attribute__((no_instrument_function)) +void trace_xmit(int stream_id, const unsigned char *data, unsigned size) +{ + if ((ITM->TCR & ITM_TCR_ITMENA) != 0UL && /* ITM tracing enabled */ + (ITM->TER & (1 << channel)) != 0UL) /* ITM channel enabled */ + { + /* collect and transmit bytes in packets of 4 bytes */ + uint32_t value = 0, shift = 0; + while (size-- > 0) { + value |= (uint32_t)*data++ << shift; + shift += 8; + if (shift >= 32) { + /* in the waiting loop, use an empty statement and not __NOP(); + although __NOP() is an inline function, it would still be + instrumented */ + while (ITM->PORT[channel].u32 == 0UL) + {} + ITM->PORT[channel].u32 = value; + value = shift = 0; + } + } + /* transmit last collected bytes */ + if (shift > 0) { + while (ITM->PORT[channel].u32 == 0UL) + {} + ITM->PORT[channel].u32 = value; + } + } +} + +__attribute__((no_instrument_function)) +void __cyg_profile_func_enter(void *this_fn, void *call_site) +{ + (void)call_site; + trace_function_profile_enter((unsigned long)this_fn); +} +__attribute__((no_instrument_function)) +void __cyg_profile_func_exit(void *this_fn, void *call_site) +{ + (void)call_site; + trace_function_profile_exit((unsigned long)this_fn); +} + diff --git a/examples/function_trace.tsdl b/examples/function_trace.tsdl new file mode 100644 index 0000000..09e8836 --- /dev/null +++ b/examples/function_trace.tsdl @@ -0,0 +1,41 @@ +/* TSDL file suitable for tracing function entries and exits. + * + * The source code must be compiled with GCC option -finstrument-functions. + * The code must also supply a function trace_xmit(), as explained in the + * book. An * important note is that trace_xmit() must be declared with + * __attribute__((no_instrument_function)) + */ +trace { + major = 1; + minor = 8; + packet.header := struct { + uint16_t magic; + }; +}; + +stream function_profile { + id = 31; + event.header := struct { + uint16_t id; + }; +}; + +typealias integer { + size = 32; + signed = false; + base = symaddress; +} := code_address; + +event function_profile::enter { + attribute = "no_instrument_function"; + fields := struct { + code_address symbol; + }; +}; + +event function_profile::exit { + attribute = "no_instrument_function"; + fields := struct { + code_address symbol; + }; +}; diff --git a/examples/traceswo.c b/examples/traceswo.c index 689cc54..7f624c6 100644 --- a/examples/traceswo.c +++ b/examples/traceswo.c @@ -1,7 +1,7 @@ /* Implementation of functions to transmit data or strings over the TRACESWO * wire of the ARM Cortex micro-controllers. * - * These routines pack the bytes to * transmit into 32-bit words, in order to + * These routines pack the bytes to transmit into 32-bit words, in order to * minimize overhead (each item that is transmitted over the TRACESWO pin is * prefixed with a 1-byte header, so that when tranmitting single bytes, each * byte has that 1-byte header overhead). diff --git a/source/Makefile.linux b/source/Makefile.linux index fa94ddf..e5117e2 100644 --- a/source/Makefile.linux +++ b/source/Makefile.linux @@ -106,18 +106,20 @@ OBJLIST_BMTRACE = bmtrace.o bmcommon.o bmp-scan.o bmp-script.o bmp-support.o \ OBJLIST_BMSCAN = bmscan.o bmp-scan.o tcpip.o +OBJLIST_CALLTREE = calltree.o + OBJLIST_POSTLINK = elf-postlink.o elf.o OBJLIST_TRACEGEN = tracegen.o parsetsdl.o -project: bmdebug bmflash bmprofile bmtrace bmscan elf-postlink tracegen +project: bmdebug bmflash bmprofile bmscan bmtrace calltree elf-postlink tracegen depend : makedepend -b -fmakefile.dep $(OBJLIST_BMDEBUG:.o=.c) $(OBJLIST_BMFLASH:.o=.c) \ - $(OBJLIST_BMPROFILE:.o=.c) $(OBJLIST_BMTRACE:.o=.c) \ - $(OBJLIST_BMSCAN:.o=.c) $(OBJLIST_POSTLINK:.o=.c) \ - $(OBJLIST_TRACEGEN:.o=.c) + $(OBJLIST_BMPROFILE:.o=.c) $(OBJLIST_BMSCAN:.o=.c) \ + $(OBJLIST_BMTRACE:.o=.c) $(OBJLIST_CALLTREE) \ + $(OBJLIST_POSTLINK:.o=.c) $(OBJLIST_TRACEGEN:.o=.c) ##### C files ##### @@ -142,6 +144,8 @@ bmp-script.o : bmp-script.c bmp-support.o : bmp-support.c +calltree.o : calltree.c + cksum.o : cksum.c crc32.o : crc32.c @@ -226,6 +230,9 @@ bmtrace : $(OBJLIST_BMTRACE) bmscan : $(OBJLIST_BMSCAN) $(LNK) $(LFLAGS) -o$@ $^ -lbsd -lpthread +calltree : $(OBJLIST_CALLTREE) + $(LNK) $(LFLAGS) -o$@ $^ -lbsd + elf-postlink : $(OBJLIST_POSTLINK) $(LNK) $(LFLAGS) -o$@ $^ -lbsd diff --git a/source/Makefile.mingw b/source/Makefile.mingw index 14522aa..7affabf 100644 --- a/source/Makefile.mingw +++ b/source/Makefile.mingw @@ -102,18 +102,21 @@ OBJLIST_BMTRACE = bmtrace.o bmcommon.o bmp-scan.o bmp-script.o bmp-support.o \ OBJLIST_BMSCAN = bmscan.o bmp-scan.o tcpip.o +OBJLIST_CALLTREE = calltree.o + OBJLIST_POSTLINK = elf-postlink.o elf.o strlcpy.o OBJLIST_TRACEGEN = tracegen.o parsetsdl.o strlcpy.o -project : bmdebug.exe bmflash.exe bmprofile.exe bmtrace.exe bmscan.exe elf-postlink.exe tracegen.exe +project : bmdebug.exe bmflash.exe bmprofile.exe bmtrace.exe bmscan.exe \ + calltree.exe elf-postlink.exe tracegen.exe depend : makedepend -b -fmakefile.dep $(OBJLIST_BMDEBUG:.o=.c) $(OBJLIST_BMFLASH:.o=.c) \ - $(OBJLIST_BMPROFILE:.o=.c) $(OBJLIST_BMTRACE:.o=.c) \ - $(OBJLIST_BMSCAN:.o=.c) $(OBJLIST_POSTLINK:.o=.c) \ - $(OBJLIST_TRACEGEN:.o=.c) + $(OBJLIST_BMPROFILE:.o=.c) $(OBJLIST_BMSCAN:.o=.c) \ + $(OBJLIST_BMTRACE:.o=.c) $(OBJLIST_CALLTREE:.o=.c) \ + $(OBJLIST_POSTLINK:.o=.c) $(OBJLIST_TRACEGEN:.o=.c) ##### C files ##### @@ -138,6 +141,8 @@ bmp-script.o : bmp-script.c bmp-support.o : bmp-support.c +calltree.o : calltree.c + cksum.o : cksum.c crc32.o : crc32.c @@ -210,6 +215,8 @@ bmdebug.res : bmdebug.rc bmflash.res : bmflash.rc +bmprofile.res : bmprofile.rc + bmtrace.res : bmtrace.rc @@ -230,6 +237,9 @@ bmtrace.exe : $(OBJLIST_BMTRACE) bmtrace.res bmscan.exe : $(OBJLIST_BMSCAN) $(LNK) $(LFLAGS) -o$@ $^ -lws2_32 +calltree.exe : $(OBJLIST_CALLTREE) + $(LNK) $(LFLAGS) -o$@ $^ + elf-postlink.exe : $(OBJLIST_POSTLINK) $(LNK) $(LFLAGS) -o$@ $^ diff --git a/source/Makefile.msvc b/source/Makefile.msvc index e7f8b89..dec5fc6 100644 --- a/source/Makefile.msvc +++ b/source/Makefile.msvc @@ -97,17 +97,20 @@ OBJLIST_BMTRACE = bmtrace.obj bmcommon.obj bmp-scan.obj bmp-script.obj bmp-suppo OBJLIST_BMSCAN = bmscan.obj bmp-scan.obj tcpip.obj +OBJLIST_CALLTREE = calltree.obj + OBJLIST_POSTLINK = elf-postlink.obj elf.obj strlcpy.obj OBJLIST_TRACEGEN = tracegen.obj parsetsdl.obj strlcpy.obj -project : bmdebug.exe bmflash.exe bmprofile.exe bmtrace.exe bmscan.exe elf-postlink.exe tracegen.exe +project : bmdebug.exe bmflash.exe bmprofile.exe bmtrace.exe bmscan.exe calltree.exe \ + elf-postlink.exe tracegen.exe depend : makedepend -b -e -o.obj -fmakefile.dep $(OBJLIST_BMDEBUG:.obj=.c) $(OBJLIST_BMFLASH:.obj=.c) \ - $(OBJLIST_BMPROFILE:.obj=.c) $(OBJLIST_BMTRACE:.obj=.c) \ - $(OBJLIST_BMSCAN:.obj=.c) $(OBJLIST_POSTLINK:.obj=.c) \ + $(OBJLIST_BMPROFILE:.obj=.c) $(OBJLIST_BMTRACE:.obj=.c) $(OBJLIST_BMSCAN:.obj=.c) \ + $(OBJLIST_CALLTREE:.obj=.c) $(OBJLIST_POSTLINK:.obj=.c) \ $(OBJLIST_TRACEGEN:.obj=.c) @@ -133,6 +136,8 @@ bmp-script.obj : bmp-script.c bmp-support.obj : bmp-support.c +calltree.obj : calltree.c + cksum.obj : cksum.c crc32.obj : crc32.c @@ -204,6 +209,8 @@ bmdebug.res : bmdebug.rc bmflash.res : bmflash.rc +bmprofile.res : bmprofile.rc + bmtrace.res : bmtrace.rc @@ -224,6 +231,9 @@ bmtrace.exe : $(OBJLIST_BMTRACE) bmtrace.res bmscan.exe : $(OBJLIST_BMSCAN) $(LNK) $(LFLAGS_C) /OUT:$@ $** advapi32.lib wsock32.lib +calltree.exe : $(OBJLIST_CALLTREE) + $(LNK) $(LFLAGS_C) /OUT:$@ $** + elf-postlink.exe : $(OBJLIST_POSTLINK) $(LNK) $(LFLAGS_C) /OUT:$@ $** diff --git a/source/bmdebug.c b/source/bmdebug.c index 9908095..bdcf2ea 100644 --- a/source/bmdebug.c +++ b/source/bmdebug.c @@ -84,6 +84,7 @@ #include "specialfolder.h" #include "svd-support.h" #include "tcpip.h" +#include "svnrev.h" #include "parsetsdl.h" #include "decodectf.h" @@ -222,22 +223,6 @@ static void stringlist_clear(STRINGLIST *root) } } -static void stringlist_delete(STRINGLIST *root, STRINGLIST *item) -{ - STRINGLIST *prev; - assert(root != NULL); - assert(item != NULL); - for (prev = root; prev->next != NULL && prev->next != item; prev = prev->next) - {} - assert(prev != NULL && prev->next == item); - if (prev->next == item) { - prev->next = item->next; - assert(item->text != NULL); - free((void*)item->text); - free((void*)item); - } -} - static unsigned stringlist_count(STRINGLIST *root) { STRINGLIST *item; @@ -1142,21 +1127,19 @@ static int console_autocomplete(char *text, size_t textsize, const DWARF_SYMBOLL return result; } -static void console_history_add(STRINGLIST *root, const char *text, int tail) +static void console_history_add(STRINGLIST *root, const char *text, bool tail) { STRINGLIST *item; assert(root != NULL); assert(text != NULL); - /* remove this text if it already appears somewhere in the list */ + /* do not add this text if it already appears earlier in the list */ for (item = root->next; item != NULL; item = item->next) { assert(item->text != NULL); if (strcmp(item->text, text) == 0) - break; /* text already appears in the list, remove it from this spot */ + return; /* text already appears in the list, no need to add it again */ } - if (item != NULL) - stringlist_delete(root, item); if (tail) stringlist_append(root, text, 0); @@ -3183,7 +3166,7 @@ static int textview_widget(struct nk_context *ctx, const char *id, int linecount = 0; /* dark background on group */ - nk_style_push_color(ctx, &ctx->style.window.fixed_background.data.color, nk_rgba(20, 29, 38, 225)); + nk_style_push_color(ctx, &ctx->style.window.fixed_background.data.color, COLOUR_BG0); nk_style_push_vec2(ctx, &ctx->style.window.group_padding, nk_vec2(6, 4)); if (nk_group_begin(ctx, id, NK_WINDOW_BORDER)) { struct nk_user_font const *font = ctx->style.font; @@ -3257,7 +3240,7 @@ static void console_widget(struct nk_context *ctx, const char *id, float rowheig struct nk_user_font const *font = ctx->style.font; /* black background on group */ - nk_style_push_color(ctx, &ctx->style.window.fixed_background.data.color, nk_rgba(20, 29, 38, 225)); + nk_style_push_color(ctx, &ctx->style.window.fixed_background.data.color, COLOUR_BG0); if (nk_group_begin(ctx, id, NK_WINDOW_BORDER)) { int lines = 0; float lineheight = 0; @@ -3276,19 +3259,19 @@ static void console_widget(struct nk_context *ctx, const char *id, float rowheig textwidth = font->width(font->userdata, font->height, item->text, strlen(item->text)) + 10; nk_layout_row_push(ctx, textwidth); if (item->flags & (STRFLG_INPUT | STRFLG_MI_INPUT)) - nk_label_colored(ctx, item->text, NK_TEXT_LEFT, nk_rgb(204, 199, 141)); + nk_label_colored(ctx, item->text, NK_TEXT_LEFT, COLOUR_FG_YELLOW); else if (item->flags & STRFLG_ERROR) - nk_label_colored(ctx, item->text, NK_TEXT_LEFT, nk_rgb(255, 80, 100)); + nk_label_colored(ctx, item->text, NK_TEXT_LEFT, COLOUR_FG_RED); else if (item->flags & STRFLG_RESULT) - nk_label_colored(ctx, item->text, NK_TEXT_LEFT, nk_rgb(64, 220, 255)); + nk_label_colored(ctx, item->text, NK_TEXT_LEFT, COLOUR_FG_BLUE); else if (item->flags & STRFLG_NOTICE) - nk_label_colored(ctx, item->text, NK_TEXT_LEFT, nk_rgb(220, 220, 128)); + nk_label_colored(ctx, item->text, NK_TEXT_LEFT, COLOUR_FG_PURPLE); else if (item->flags & STRFLG_STATUS) - nk_label_colored(ctx, item->text, NK_TEXT_LEFT, nk_rgb(255, 255, 128)); + nk_label_colored(ctx, item->text, NK_TEXT_LEFT, COLOUR_FG_YELLOW); else if (item->flags & STRFLG_EXEC) - nk_label_colored(ctx, item->text, NK_TEXT_LEFT, nk_rgb(128, 222, 128)); + nk_label_colored(ctx, item->text, NK_TEXT_LEFT, COLOUR_FG_GREEN); else if (item->flags & STRFLG_LOG) - nk_label_colored(ctx, item->text, NK_TEXT_LEFT, nk_rgb(128, 222, 222)); + nk_label_colored(ctx, item->text, NK_TEXT_LEFT, COLOUR_FG_AQUA); else nk_label(ctx, item->text, NK_TEXT_LEFT); nk_layout_row_end(ctx); @@ -3355,7 +3338,7 @@ static int line_phys2source(int fileindex, int phys_line) assert(fileindex >= 0); int line = 1; SOURCELINE *item; - for (item = source_firstline(fileindex); item != NULL && phys_line > 1; item = item->next) { + for (item = source_firstline(fileindex); item != NULL && phys_line > 0; item = item->next) { if (item->hidden) continue; if (item->linenumber > 0) @@ -3408,7 +3391,7 @@ static void source_widget(struct nk_context *ctx, const char *id, float rowheigh font = ctx->style.font; /* black background on group */ - nk_style_push_color(ctx, &ctx->style.window.fixed_background.data.color, nk_rgba(20, 29, 38, 225)); + nk_style_push_color(ctx, &ctx->style.window.fixed_background.data.color, COLOUR_BG0); if (nk_group_begin(ctx, id, NK_WINDOW_BORDER)) { int lines = 0, maxlen = 0; float maxwidth = 0; @@ -3432,20 +3415,20 @@ static void source_widget(struct nk_context *ctx, const char *id, float rowheigh assert(bkpt != NULL); stbtn.normal.data.color = stbtn.hover.data.color = stbtn.active.data.color = stbtn.text_background - = nk_rgba(20, 29, 38, 225); + = COLOUR_BG0; if (bkpt->enabled) - stbtn.text_normal = stbtn.text_active = stbtn.text_hover = nk_rgb(140, 25, 50); + stbtn.text_normal = stbtn.text_active = stbtn.text_hover = COLOUR_BG_RED; else - stbtn.text_normal = stbtn.text_active = stbtn.text_hover = nk_rgb(255, 50, 120); + stbtn.text_normal = stbtn.text_active = stbtn.text_hover = COLOUR_BG_RED; nk_button_symbol_styled(ctx, &stbtn, bkpt->enabled ? NK_SYMBOL_CIRCLE_SOLID : NK_SYMBOL_CIRCLE_OUTLINE); } else if (item->linenumber != 0) { nk_layout_row_push(ctx, 2 * rowheight); char str[20]; sprintf(str, "%4d", item->linenumber); if (grayed) - nk_label_colored(ctx, str, NK_TEXT_LEFT, nk_rgb(128, 128, 128)); + nk_label_colored(ctx, str, NK_TEXT_LEFT, COLOUR_FG_GRAY); else if (lines == source_cursorline) - nk_label_colored(ctx, str, NK_TEXT_LEFT, nk_rgb(250, 250, 128)); + nk_label_colored(ctx, str, NK_TEXT_LEFT, COLOUR_FG_YELLOW); else nk_label(ctx, str, NK_TEXT_LEFT); } else { @@ -3464,8 +3447,8 @@ static void source_widget(struct nk_context *ctx, const char *id, float rowheigh if (is_exec_point) { stbtn.normal.data.color = stbtn.hover.data.color = stbtn.active.data.color = stbtn.text_background - = nk_rgba(20, 29, 38, 225); - stbtn.text_normal = stbtn.text_active = stbtn.text_hover = nk_rgb(250, 250, 128); + = COLOUR_BG0; + stbtn.text_normal = stbtn.text_active = stbtn.text_hover = COLOUR_FG_YELLOW; nk_button_symbol_styled(ctx, &stbtn, NK_SYMBOL_TRIANGLE_RIGHT); } else { nk_spacing(ctx, 1); @@ -3479,11 +3462,11 @@ static void source_widget(struct nk_context *ctx, const char *id, float rowheigh } nk_layout_row_push(ctx, textwidth + 10); if (grayed) - nk_label_colored(ctx, item->text, NK_TEXT_LEFT, nk_rgb(128, 128, 128)); + nk_label_colored(ctx, item->text, NK_TEXT_LEFT, COLOUR_FG_GRAY); else if (lines == source_cursorline) - nk_label_colored(ctx, item->text, NK_TEXT_LEFT, nk_rgb(250, 250, 128)); + nk_label_colored(ctx, item->text, NK_TEXT_LEFT, COLOUR_FG_YELLOW); else if (item->linenumber == 0) - nk_label_colored(ctx, item->text, NK_TEXT_LEFT, nk_rgb(192, 192, 224)); + nk_label_colored(ctx, item->text, NK_TEXT_LEFT, COLOUR_FG_AQUA); else nk_label(ctx, item->text, NK_TEXT_LEFT); nk_layout_row_end(ctx); @@ -3816,10 +3799,9 @@ static bool load_targetoptions(const char *filename, char *entrypoint, size_t en swo->init_status = 0; ini_gets("SWO trace", "ctf", "", swo->metadata, sizearray(swo->metadata), filename); for (idx = 0; idx < NUM_CHANNELS; idx++) { - #define SWO_TRACE_DEFAULT_COLOR 190 char key[41], value[128]; /* preset: port 0 is enabled by default, others disabled by default */ - channel_set(idx, (idx == 0), NULL, nk_rgb(SWO_TRACE_DEFAULT_COLOR, SWO_TRACE_DEFAULT_COLOR, SWO_TRACE_DEFAULT_COLOR)); + channel_set(idx, (idx == 0), NULL, SWO_TRACE_DEFAULT_COLOR); sprintf(key, "chan%d", idx); unsigned clr; int enabled; @@ -4594,7 +4576,8 @@ static void trace_info_channel(int ch_start, int ch_end, STRINGLIST *textroot) strlcat(msg, "\"", sizearray(msg)); } struct nk_color clr = channel_getcolor(chan); - if (clr.r != SWO_TRACE_DEFAULT_COLOR || clr.g != SWO_TRACE_DEFAULT_COLOR || clr.b != SWO_TRACE_DEFAULT_COLOR) { + struct nk_color defclr = SWO_TRACE_DEFAULT_COLOR; + if (clr.r != defclr.r || clr.g != defclr.g || clr.b != defclr.b) { char str[30]; sprintf(str, " #%02x%02x%02x", clr.r, clr.g, clr.b); strlcat(msg, str, sizearray(msg)); @@ -4747,9 +4730,10 @@ static int handle_trace_cmd(const char *command, SWOSETTINGS *swo) if (*ptr == '\0' || TERM_EQU(ptr, "info", 4)) return 3; /* if only "trace" is typed, interpret it as "trace info" */ - if ((ptr = strstr(cmdcopy, "clear")) != NULL && TERM_END(ptr, 5)) { + char *opt_clear; + if ((opt_clear = strstr(cmdcopy, "clear")) != NULL && TERM_END(opt_clear, 5)) { tracestring_clear(); - memset(ptr, ' ', 5); /* erase the parameter from the string */ + memset(opt_clear, ' ', 5); /* erase the "clear" parameter from the string */ } assert(swo != NULL); @@ -5114,7 +5098,22 @@ static void usage(const char *invalid_option) "Options:\n" "-f=value Font size to use (value must be 8 or larger).\n" "-g=path Path to the GDB executable to use.\n" - "-h This help.\n"); + "-h This help.\n" + "-v Show version information.\n"); +} + +static void version(void) +{ + #if defined _WIN32 /* fix console output on Windows */ + if (AttachConsole(ATTACH_PARENT_PROCESS)) { + freopen("CONOUT$", "wb", stdout); + freopen("CONOUT$", "wb", stderr); + } + printf("\n"); + #endif + + printf("BMDebug version 1.1.%d.\n", SVNREV_NUM); + printf("Copyright 2019-2022 CompuPhase\nLicensed under the Apache License version 2.0\n"); } static void config_read_tabstate(const char *key, enum nk_collapse_states *state, SIZERBAR *sizer, @@ -5200,6 +5199,7 @@ typedef struct tagAPPSTATE { unsigned long scriptparams[4];/**< parameters for running configuration scripts (for TRACESWO) */ const char **sourcefiles; /**< array of all source files */ bool disassemble_mode; /**< whether source code is mixed with disassembly */ + bool dwarf_loaded; /**< whether DWARF info is loaded */ int prev_clicked_line; /**< line in source view that was previously clicked on (to detect multiple clicks on the same line) */ char statesymbol[128]; /**< name of the symbol hovered over (in source view) */ char ttipvalue[256]; /**< text for variable value in tooltip (when hovering above symbol) */ @@ -5596,8 +5596,12 @@ static void console_view(struct nk_context *ctx, APPSTATE *state, ctf_parse_cleanup(); ctf_decode_cleanup(); ctf_error_notify(CTFERR_NONE, 0, NULL); - if (!ctf_parse_init(state->swo.metadata) || !ctf_parse_run()) + if (ctf_parse_init(state->swo.metadata) && ctf_parse_run()) { + if (state->dwarf_loaded) + ctf_set_symtable(&dwarf_symboltable); + } else { ctf_parse_cleanup(); + } } serial_info_mode(NULL); tab_states[TAB_SERMON] = NK_MAXIMIZED; /* make sure the serial monitor view is open */ @@ -5660,8 +5664,10 @@ static void console_view(struct nk_context *ctx, APPSTATE *state, TERM_EQU(state->console_edit, "disable", 7) || TERM_EQU(state->console_edit, "enable", 6)) state->refreshflags |= REFRESH_BREAKPOINTS | IGNORE_DOUBLE_DONE; - /* save console_edit in a recent command list */ - stringlist_insert(&state->consoleedit_root, state->console_edit, 0); + /* save console_edit in a recent command list (but skip this if the + command is already at the head of the list) */ + if (state->consoleedit_root.next != NULL && strcmp(state->consoleedit_root.next->text, state->console_edit) != 0) + stringlist_insert(&state->consoleedit_root, state->console_edit, 0); state->consoleedit_next = NULL; state->console_edit[0] = '\0'; } @@ -5753,13 +5759,12 @@ static void panel_configuration(struct nk_context *ctx, APPSTATE *state, basename, sizearray(basename), nk_filter_ascii, tooltip); nk_layout_row_push(ctx, BROWSEBTN_WIDTH); if (nk_button_symbol(ctx, NK_SYMBOL_TRIPLE_DOT)) { - const char *s = noc_file_dialog_open(NOC_FILE_DIALOG_OPEN, - "Executables\0*.elf;*.\0All files\0*.*\0", - NULL, state->GDBpath, "Select GDB program", - guidriver_apphandle()); - if (s != NULL && strlen(s) < sizearray(state->GDBpath)) { - strlcpy(state->GDBpath, s, sizearray(state->GDBpath)); - free((void*)s); + int res = noc_file_dialog_open(state->GDBpath, sizearray(state->GDBpath), + NOC_FILE_DIALOG_OPEN, + "Executables\0*.elf;*.\0All files\0*.*\0", + NULL, state->GDBpath, "Select GDB program", + guidriver_apphandle()); + if (res) { task_close(&state->task); /* terminate running instance of GDB */ RESETSTATE(state, STATE_INIT); } @@ -5779,14 +5784,13 @@ static void panel_configuration(struct nk_context *ctx, APPSTATE *state, nk_layout_row_push(ctx, BROWSEBTN_WIDTH); if (nk_button_symbol(ctx, NK_SYMBOL_TRIPLE_DOT)) { translate_path(state->ELFfile, 1); - const char *s = noc_file_dialog_open(NOC_FILE_DIALOG_OPEN, - "ELF Executables\0*.elf;*.bin;*.\0All files\0*.*\0", - NULL, state->ELFfile, "Select ELF Executable", - guidriver_apphandle()); - if (s != NULL && strlen(s) < sizearray(state->ELFfile)) { - strcpy(state->ELFfile, s); + int res = noc_file_dialog_open(state->ELFfile, sizearray(state->ELFfile), + NOC_FILE_DIALOG_OPEN, + "ELF Executables\0*.elf;*.bin;*.\0All files\0*.*\0", + NULL, state->ELFfile, "Select ELF Executable", + guidriver_apphandle()); + if (res) { translate_path(state->ELFfile, 0); - free((void*)s); if (state->curstate > STATE_FILE) RESETSTATE(state, STATE_FILE); } @@ -5818,13 +5822,12 @@ static void panel_configuration(struct nk_context *ctx, APPSTATE *state, nk_layout_row_push(ctx, BROWSEBTN_WIDTH); if (nk_button_symbol(ctx, NK_SYMBOL_TRIPLE_DOT)) { translate_path(state->SVDfile, 1); - const char *s = noc_file_dialog_open(NOC_FILE_DIALOG_OPEN, - "CMSIS SVD files\0*.svd\0All files\0*.*\0", - NULL, state->SVDfile, "Select CMSIS SVD file", - guidriver_apphandle()); - if (s != NULL && strlen(s) < sizearray(state->SVDfile)) { - strcpy(state->SVDfile, s); - free((void*)s); + int res = noc_file_dialog_open(state->SVDfile, sizearray(state->SVDfile), + NOC_FILE_DIALOG_OPEN, + "CMSIS SVD files\0*.svd\0All files\0*.*\0", + NULL, state->SVDfile, "Select CMSIS SVD file", + guidriver_apphandle()); + if (res) { if (state->curstate > STATE_GET_SOURCES) { svd_clear(); if (strlen(state->SVDfile) >0) @@ -5900,7 +5903,7 @@ static void panel_breakpoints(struct nk_context *ctx, APPSTATE *state, } nk_layout_row_dynamic(ctx, state->sizerbar_breakpoints.size, 1); - nk_style_push_color(ctx, &ctx->style.window.fixed_background.data.color, nk_rgba(20, 29, 38, 225)); + nk_style_push_color(ctx, &ctx->style.window.fixed_background.data.color, COLOUR_BG0); if (nk_group_begin(ctx, "breakpoints", 0)) { for (BREAKPOINT *bp = breakpoint_root.next; bp != NULL; bp = bp->next) { int en; @@ -5948,7 +5951,7 @@ static int label_formatmenu(struct nk_context *ctx, const char *text, int change { struct nk_rect bounds = nk_layout_widget_bounds(ctx); if (changeflag) - nk_label_colored(ctx, text, NK_TEXT_LEFT, nk_rgb(255, 100, 128)); + nk_label_colored(ctx, text, NK_TEXT_LEFT, COLOUR_FG_RED); else nk_label(ctx, text, NK_TEXT_LEFT); @@ -6044,7 +6047,7 @@ static void panel_locals(struct nk_context *ctx, APPSTATE *state, } nk_layout_row_dynamic(ctx, state->sizerbar_locals.size, 1); - nk_style_push_color(ctx, &ctx->style.window.fixed_background.data.color, nk_rgba(20, 29, 38, 225)); + nk_style_push_color(ctx, &ctx->style.window.fixed_background.data.color, COLOUR_BG0); if (nk_group_begin(ctx, "locals", 0)) { for (LOCALVAR *var = localvar_root.next; var != NULL; var = var->next) { nk_layout_row_begin(ctx, NK_STATIC, rowheight, 2); @@ -6107,7 +6110,7 @@ static void panel_watches(struct nk_context *ctx, APPSTATE *state, } nk_layout_row_dynamic(ctx, state->sizerbar_watches.size, 1); - nk_style_push_color(ctx, &ctx->style.window.fixed_background.data.color, nk_rgba(20, 29, 38, 225)); + nk_style_push_color(ctx, &ctx->style.window.fixed_background.data.color, COLOUR_BG0); if (nk_group_begin(ctx, "watches", 0)) { for (WATCH *watch = watch_root.next; watch != NULL; watch = watch->next) { nk_layout_row_begin(ctx, NK_STATIC, rowheight, 4); @@ -6185,7 +6188,7 @@ static void panel_registers(struct nk_context *ctx, APPSTATE *state, float namewidth = 2 * ROW_HEIGHT; float valwidth = 4 * ROW_HEIGHT; nk_layout_row_dynamic(ctx, state->sizerbar_registers.size, 1); - nk_style_push_color(ctx, &ctx->style.window.fixed_background.data.color, nk_rgba(20, 29, 38, 225)); + nk_style_push_color(ctx, &ctx->style.window.fixed_background.data.color, COLOUR_BG0); if (nk_group_begin(ctx, "registers", 0)) { for (int idx = 0; idx < sizearray(register_def); idx++) { nk_layout_row_begin(ctx, NK_STATIC, rowheight, 2); @@ -6196,7 +6199,7 @@ static void panel_registers(struct nk_context *ctx, APPSTATE *state, char field[20]; sprintf(field, "0x%08lx", register_def[idx].value); if (register_def[idx].flags & REGFLG_CHANGED) - nk_label_colored(ctx, field, NK_TEXT_LEFT, nk_rgb(255, 100, 128)); + nk_label_colored(ctx, field, NK_TEXT_LEFT, COLOUR_FG_RED); else nk_label(ctx, field, NK_TEXT_LEFT); guidriver_setfont(ctx, fonttype); @@ -6229,7 +6232,7 @@ static void panel_memory(struct nk_context *ctx, APPSTATE *state, } else { nk_layout_row_dynamic(ctx, ROW_HEIGHT, 1); if (state->memdump.message != NULL) - nk_label_colored(ctx, state->memdump.message, NK_TEXT_ALIGN_CENTERED | NK_TEXT_ALIGN_MIDDLE, nk_rgb(255, 100, 128)); + nk_label_colored(ctx, state->memdump.message, NK_TEXT_ALIGN_CENTERED | NK_TEXT_ALIGN_MIDDLE, COLOUR_FG_RED); else nk_label(ctx, "Use \"x\" command to view memory", NK_TEXT_ALIGN_CENTERED | NK_TEXT_ALIGN_MIDDLE); } @@ -6245,9 +6248,9 @@ static void panel_semihosting(struct nk_context *ctx, APPSTATE *state, assert(tab_state != NULL); /* highlight tab text if new content arrives and the tab is closed */ - int highlight = !(*tab_state) && stringlist_count(&semihosting_root) != state->semihosting_lines; + bool highlight = !(*tab_state) && stringlist_count(&semihosting_root) != state->semihosting_lines; if (highlight) - nk_style_push_color(ctx,&ctx->style.tab.text, nk_rgb(255, 255, 160)); + nk_style_push_color(ctx,&ctx->style.tab.text, COLOUR_FG_YELLOW); int result = nk_tree_state_push(ctx, NK_TREE_TAB, "Semihosting output", tab_state); if (highlight) nk_style_pop_color(ctx); @@ -6255,7 +6258,7 @@ static void panel_semihosting(struct nk_context *ctx, APPSTATE *state, nk_sizer_refresh(&state->sizerbar_semihosting); if (result) { nk_layout_row_dynamic(ctx, state->sizerbar_semihosting.size, 1); - nk_style_push_color(ctx, &ctx->style.window.fixed_background.data.color, nk_rgba(20, 29, 38, 225)); + nk_style_push_color(ctx, &ctx->style.window.fixed_background.data.color, COLOUR_BG0); if (nk_group_begin(ctx, "semihosting", 0)) { STRINGLIST *item; state->semihosting_lines = 0; @@ -6280,9 +6283,9 @@ static void panel_serialmonitor(struct nk_context *ctx, APPSTATE *state, assert(tab_state != NULL); /* highlight tab text if new content arrives and the tab is closed */ - int highlight = !(*tab_state) && sermon_countlines() != state->sermon_lines; + bool highlight = !(*tab_state) && sermon_countlines() != state->sermon_lines; if (highlight) - nk_style_push_color(ctx,&ctx->style.tab.text, nk_rgb(255, 255, 160)); + nk_style_push_color(ctx,&ctx->style.tab.text, COLOUR_FG_YELLOW); int result = nk_tree_state_push(ctx, NK_TREE_TAB, "Serial console", tab_state); if (highlight) nk_style_pop_color(ctx); @@ -6291,7 +6294,7 @@ static void panel_serialmonitor(struct nk_context *ctx, APPSTATE *state, if (result) { nk_layout_row_dynamic(ctx, state->sizerbar_serialmon.size, 1); struct nk_rect bounds = nk_layout_widget_bounds(ctx); - nk_style_push_color(ctx, &ctx->style.window.fixed_background.data.color, nk_rgba(20, 29, 38, 225)); + nk_style_push_color(ctx, &ctx->style.window.fixed_background.data.color, COLOUR_BG0); if (nk_group_begin(ctx, "serial", 0)) { struct nk_user_font const *font = ctx->style.font; int linecount = 0; @@ -6314,7 +6317,7 @@ static void panel_serialmonitor(struct nk_context *ctx, APPSTATE *state, linecount += 1; } if (!sermon_isopen()) { - struct nk_color clr = nk_rgb(255, 80, 100); + struct nk_color clr = COLOUR_FG_RED; nk_layout_row_dynamic(ctx, opt_fontsize, 1); nk_label_colored(ctx, "No port opened", NK_TEXT_LEFT, clr); linecount += 1; @@ -6344,9 +6347,9 @@ static void panel_traceswo(struct nk_context *ctx, APPSTATE *state, assert(tab_state != NULL); /* highlight tab text if new content arrives and the tab is closed */ - int highlight = !(*tab_state) && tracestring_count() != state->swo_lines; + bool highlight = !(*tab_state) && tracestring_count() != state->swo_lines; if (highlight) - nk_style_push_color(ctx,&ctx->style.tab.text, nk_rgb(255, 255, 160)); + nk_style_push_color(ctx,&ctx->style.tab.text, COLOUR_FG_YELLOW); int result = nk_tree_state_push(ctx, NK_TREE_TAB, "SWO tracing", tab_state); if (highlight) nk_style_pop_color(ctx); @@ -6441,16 +6444,12 @@ static void handle_stateaction(APPSTATE *state, const enum nk_collapse_states ta #else const char *filter = "Executables\0*\0All files\0*\0"; #endif - const char *s = noc_file_dialog_open(NOC_FILE_DIALOG_OPEN, filter, - NULL, state->GDBpath, "Select GDB Executable", - guidriver_apphandle()); - if (s != NULL && strlen(s) < sizearray(state->GDBpath)) { - strcpy(state->GDBpath, s); - free((void*)s); - assert(state->curstate == STATE_GDB_TASK); /* drop back into this case after */ - } else { + int res = noc_file_dialog_open(state->GDBpath, sizearray(state->GDBpath), + NOC_FILE_DIALOG_OPEN, filter, + NULL, state->GDBpath, "Select GDB Executable", + guidriver_apphandle()); + if (!res) RESETSTATE(state, STATE_QUIT); /* selection dialog was canceled, quit the front-end */ - } } break; case STATE_SCAN_BMP: @@ -6534,7 +6533,8 @@ static void handle_stateaction(APPSTATE *state, const enum nk_collapse_states ta &state->tpwr, &state->connect_srst, &state->autodownload, state->SVDfile, sizearray(state->SVDfile), &state->swo); /* load target filename in GDB */ - snprintf(state->cmdline, CMD_BUFSIZE, "-file-exec-and-symbols %s\n", enquote(temp, state->ELFfile, sizeof(temp))); + snprintf(state->cmdline, CMD_BUFSIZE, "-file-exec-and-symbols %s\n", + enquote(temp, state->ELFfile, sizeof(temp))); if (task_stdin(&state->task, state->cmdline)) console_input(state->cmdline); state->atprompt = nk_false; @@ -6545,7 +6545,9 @@ static void handle_stateaction(APPSTATE *state, const enum nk_collapse_states ta FILE *fp = fopen(state->ELFfile, "rb"); if (fp != NULL) { int address_size; - if (!dwarf_read(fp, &dwarf_linetable, &dwarf_symboltable, &dwarf_filetable, &address_size)) + state->dwarf_loaded = dwarf_read(fp, &dwarf_linetable, &dwarf_symboltable, + &dwarf_filetable, &address_size); + if (!state->dwarf_loaded) console_add("No DWARF debug information\n", STRFLG_ERROR); fclose(fp); } @@ -6557,8 +6559,12 @@ static void handle_stateaction(APPSTATE *state, const enum nk_collapse_states ta ctf_parse_cleanup(); ctf_decode_cleanup(); ctf_error_notify(CTFERR_NONE, 0, NULL); - if (!ctf_parse_init(state->swo.metadata) || !ctf_parse_run()) + if (ctf_parse_init(state->swo.metadata) && ctf_parse_run()) { + if (state->dwarf_loaded) + ctf_set_symtable(&dwarf_symboltable); + } else { ctf_parse_cleanup(); + } } source_cursorfile = source_cursorline = 0; source_execfile = source_execline = 0; @@ -7283,8 +7289,10 @@ static void handle_stateaction(APPSTATE *state, const enum nk_collapse_states ta && ctf_findmetadata(state->ELFfile, state->swo.metadata, sizearray(state->swo.metadata)) && ctf_parse_init(state->swo.metadata) && ctf_parse_run()) { - const CTF_STREAM *stream; + if (state->dwarf_loaded) + ctf_set_symtable(&dwarf_symboltable); /* stream names overrule configured channel names */ + const CTF_STREAM *stream; for (int idx = 0; (stream = stream_by_seqnr(idx)) != NULL; idx++) if (stream->name != NULL && strlen(stream->name) > 0) channel_setname(idx, stream->name); @@ -7510,7 +7518,7 @@ int main(int argc, char *argv[]) appstate.popup_active = POPUP_NONE; appstate.cmdline = malloc(CMD_BUFSIZE * sizeof(char)); if (appstate.cmdline == NULL) - return 1; + return EXIT_FAILURE; /* locate the configuration file for settings */ char txtConfigFile[_MAX_PATH]; get_configfile(txtConfigFile, sizearray(txtConfigFile), "bmdebug.ini"); @@ -7565,7 +7573,7 @@ int main(int argc, char *argv[]) ini_gets("Commands", key, "", appstate.console_edit, sizearray(appstate.console_edit), txtConfigFile); if (strlen(appstate.console_edit) == 0) break; - console_history_add(&appstate.consoleedit_root, appstate.console_edit, 1); + console_history_add(&appstate.consoleedit_root, appstate.console_edit, true); } strcpy(appstate.EntryPoint, "main"); @@ -7577,7 +7585,7 @@ int main(int argc, char *argv[]) case '?': case 'h': usage(NULL); - return 0; + return EXIT_SUCCESS; case 'f': ptr = &argv[idx][2]; if (*ptr == '=' || *ptr == ':') @@ -7602,6 +7610,9 @@ int main(int argc, char *argv[]) ptr++; strlcpy(appstate.GDBpath, ptr, sizearray(appstate.GDBpath)); break; + case 'v': + version(); + return EXIT_SUCCESS; default: usage(argv[idx]); return EXIT_FAILURE; @@ -7772,14 +7783,16 @@ int main(int argc, char *argv[]) pointer_setstyle(CURSOR_UPDOWN); else if (splitter_hor.hover) pointer_setstyle(CURSOR_LEFTRIGHT); +#if defined __linux__ else pointer_setstyle(CURSOR_NORMAL); +#endif } /* window */ nk_end(ctx); /* Draw */ - guidriver_render(nk_rgb(30,30,30)); + guidriver_render(COLOUR_BG0_S); } exitcode = task_close(&appstate.task); diff --git a/source/bmdebug.rc b/source/bmdebug.rc index 7ec1530..0e45b85 100644 --- a/source/bmdebug.rc +++ b/source/bmdebug.rc @@ -19,6 +19,7 @@ #include #include +#include "svnrev.h" AppIcon ICON "res/icon_debug.ico" @@ -28,9 +29,9 @@ AppIcon ICON "res/icon_debug.ico" * for details on version information and the VERSIONINFO structure. */ #define VERSION 1 -#define REVISION 0 -#define BUILD 0 -#define VERSIONSTR "1.0.0\0" +#define REVISION 1 +#define BUILD SVNREV_NUM +#define VERSIONSTR "1.1.*\0" #define VERSIONNAME "bmdebug.exe\0" #define VERSIONDESCRIPTION "GDB front-end\0" #define VERSIONCOMPANYNAME "CompuPhase\0" diff --git a/source/bmflash.c b/source/bmflash.c index 3adba58..5291e22 100644 --- a/source/bmflash.c +++ b/source/bmflash.c @@ -73,6 +73,7 @@ #include "rs232.h" #include "tcpip.h" #include "specialfolder.h" +#include "svnrev.h" #if defined __linux__ || defined __unix__ #include "res/icon_download_64.h" @@ -152,7 +153,7 @@ static int log_widget(struct nk_context *ctx, const char *id, const char *conten /* black background on group */ bkgnd = stwin->fixed_background; - stwin->fixed_background = nk_style_item_color(nk_rgb(20, 29, 38)); + stwin->fixed_background = nk_style_item_color(COLOUR_BG0); if (nk_group_begin(ctx, id, NK_WINDOW_BORDER)) { float lineheight = 0; const char *head = content; @@ -167,19 +168,19 @@ static int log_widget(struct nk_context *ctx, const char *id, const char *conten lineheight = rcline.h; } if (*head == '^' && isdigit(*(head + 1))) { - struct nk_color clr = nk_rgb(205, 201, 171); + struct nk_color clr = COLOUR_TEXT; switch (*(head + 1)) { case '1': /* error (red) */ - clr = nk_rgb(255, 80, 100); + clr = COLOUR_FG_RED; break; case '2': /* ok (green) */ - clr = nk_rgb(100, 255, 100); + clr = COLOUR_FG_GREEN; break; case '3': /* warning (yellow) */ - clr = nk_rgb(255, 240, 80); + clr = COLOUR_FG_YELLOW; break; - case '4': /* notice (white) */ - clr = nk_rgb(255, 255, 255); + case '4': /* notice (highlighted) */ + clr = COLOUR_HIGHLIGHT; break; } nk_text_colored(ctx, head + 2, (int)(tail - head - 2), NK_TEXT_LEFT, clr); @@ -630,7 +631,22 @@ static void usage(const char *invalid_option) printf("Usage: bmflash [options] elf-file\n\n" "Options:\n" "-f=value Font size to use (value must be 8 or larger).\n" - "-h This help.\n"); + "-h This help." + "-v Show version information.\n"); +} + +static void version(void) +{ + #if defined _WIN32 /* fix console output on Windows */ + if (AttachConsole(ATTACH_PARENT_PROCESS)) { + freopen("CONOUT$", "wb", stdout); + freopen("CONOUT$", "wb", stderr); + } + printf("\n"); + #endif + + printf("BMFlash version 1.1.%d.\n", SVNREV_NUM); + printf("Copyright 2019-2022 CompuPhase\nLicensed under the Apache License version 2.0\n"); } static int help_popup(struct nk_context *ctx) @@ -1026,13 +1042,10 @@ static void panel_options(struct nk_context *ctx, APPSTATE *state, #else const char *filter = "Executables\0*\0All files\0*\0"; #endif - const char *s = noc_file_dialog_open(NOC_FILE_DIALOG_OPEN, filter, - NULL, state->PostProcess, - "Select Executable", guidriver_apphandle()); - if (s != NULL && strlen(s) < sizearray(state->PostProcess)) { - strcpy(state->PostProcess, s); - free((void*)s); - } + noc_file_dialog_open(state->PostProcess, sizearray(state->PostProcess), + NOC_FILE_DIALOG_OPEN, filter, + NULL, state->PostProcess, + "Select Executable", guidriver_apphandle()); } nk_layout_row_dynamic(ctx, ROW_HEIGHT, 1); @@ -1463,7 +1476,7 @@ int main(int argc, char *argv[]) case '?': case 'h': usage(NULL); - return 0; + return EXIT_SUCCESS; case 'f': ptr = &argv[idx][2]; if (*ptr == '=' || *ptr == ':') @@ -1482,6 +1495,9 @@ int main(int argc, char *argv[]) strlcpy(opt_fontmono, mono, sizearray(opt_fontmono)); } break; + case 'v': + version(); + return EXIT_SUCCESS; default: usage(argv[idx]); return EXIT_FAILURE; @@ -1538,15 +1554,13 @@ int main(int argc, char *argv[]) load_options = 2; nk_layout_row_push(ctx, BROWSEBTN_WIDTH); if (nk_button_symbol(ctx, NK_SYMBOL_TRIPLE_DOT) || nk_input_is_key_pressed(&ctx->input, NK_KEY_OPEN)) { - const char *s = noc_file_dialog_open(NOC_FILE_DIALOG_OPEN, - "ELF Executables\0*.elf;*.bin;*.\0All files\0*.*\0", - NULL, NULL, "Select ELF Executable", - guidriver_apphandle()); - if (s != NULL && strlen(s) < sizearray(appstate.ELFfile)) { - strcpy(appstate.ELFfile, s); + int res = noc_file_dialog_open(appstate.ELFfile, sizearray(appstate.ELFfile), + NOC_FILE_DIALOG_OPEN, + "ELF Executables\0*.elf;*.bin;*.\0All files\0*.*\0", + NULL, NULL, "Select ELF Executable", + guidriver_apphandle()); + if (res) load_options = 2; - free((void*)s); - } } nk_layout_row_end(ctx); @@ -1649,7 +1663,7 @@ int main(int argc, char *argv[]) nk_end(ctx); /* Draw */ - guidriver_render(nk_rgb(30,30,30)); + guidriver_render(COLOUR_BG0_S); } if (strlen(appstate.ParamFile) > 0 && access(appstate.ParamFile, 0) == 0) diff --git a/source/bmflash.rc b/source/bmflash.rc index 2315e73..a226cb2 100644 --- a/source/bmflash.rc +++ b/source/bmflash.rc @@ -20,6 +20,7 @@ #include #include +#include "svnrev.h" AppIcon ICON "res/icon_download.ico" @@ -29,9 +30,9 @@ AppIcon ICON "res/icon_download.ico" * for details on version information and the VERSIONINFO structure. */ #define VERSION 1 -#define REVISION 0 -#define BUILD 0 -#define VERSIONSTR "1.0.0\0" +#define REVISION 1 +#define BUILD SVNREV_NUM +#define VERSIONSTR "1.0.*\0" #define VERSIONNAME "bmflash.exe\0" #define VERSIONDESCRIPTION "Flash programming tool\0" #define VERSIONCOMPANYNAME "CompuPhase\0" diff --git a/source/bmp-script.c b/source/bmp-script.c index 9aaa577..42f24c1 100644 --- a/source/bmp-script.c +++ b/source/bmp-script.c @@ -237,7 +237,7 @@ static const SCRIPT_DEF script_defaults[] = { }, /* swo_close (generic) */ - { "swo_channels", "*", + { "swo_close", "*", "SCB_DEMCR = 0 \n" "ITM_LAR = 0xC5ACCE55 \n" /* unlock access to ITM registers */ "ITM_TCR = 0 \n" diff --git a/source/bmprofile.c b/source/bmprofile.c index 4d7c8cd..9ab7b25 100644 --- a/source/bmprofile.c +++ b/source/bmprofile.c @@ -49,6 +49,7 @@ #include "bmp-script.h" #include "bmp-scan.h" #include "bmp-support.h" +#include "demangle.h" #include "dwarf.h" #include "elf.h" #include "gdb-rsp.h" @@ -62,6 +63,7 @@ #include "nuklear_tooltip.h" #include "swotrace.h" #include "tcpip.h" +#include "svnrev.h" #if defined __linux__ || defined __unix__ #include "res/icon_profile_64.h" @@ -131,7 +133,22 @@ static void usage(const char *invalid_option) "Options:\n" "-f=value Font size to use (value must be 8 or larger).\n" "-h This help.\n\n" - "filename Path to the ELF file to profile (must contain debug info).\n"); + "filename Path to the ELF file to profile (must contain debug info).\n" + "-v Show version information.\n"); +} + +static void version(void) +{ + #if defined _WIN32 /* fix console output on Windows */ + if (AttachConsole(ATTACH_PARENT_PROCESS)) { + freopen("CONOUT$", "wb", stdout); + freopen("CONOUT$", "wb", stderr); + } + printf("\n"); + #endif + + printf("BMProfile version 1.1.%d.\n", SVNREV_NUM); + printf("Copyright 2022 CompuPhase\nLicensed under the Apache License version 2.0\n"); } enum { @@ -193,6 +210,7 @@ typedef struct tagAPPSTATE { bool connected; /**< connected to BMP? */ bool attached; /**< attached to target? */ bool dwarf_loaded; /**< whether DWARF information is valid */ + bool init_done; /**< whether target & BMP have been initialized (assuming init_target is set) */ bool firstrun; /**< is this the first run after a connect? */ int view; /**< histogram or function list */ char refreshrate_str[16]; /**< edit buffer for refresh intreval */ @@ -239,11 +257,12 @@ static bool save_settings(const char *filename, const APPSTATE *state, const enum nk_collapse_states tab_states[], const SPLITTERBAR *splitter_hor) { + assert(filename != NULL); assert(state != NULL); assert(tab_states != NULL); assert(splitter_hor != NULL); - if (filename == NULL || strlen(filename) == 0) + if (strlen(filename) == 0) return false; ini_putl("Settings", "init-target", state->init_target, filename); @@ -266,13 +285,11 @@ static bool load_settings(const char *filename, APPSTATE *state, enum nk_collapse_states tab_states[], SPLITTERBAR *splitter_hor) { + assert(filename != NULL); assert(state != NULL); assert(tab_states != NULL); assert(splitter_hor != NULL); - if (filename == NULL || strlen(filename) == 0 || access(filename, 0) != 0) - return false; - state->init_target = (int)ini_getl("Settings", "init-target", 1, filename); state->init_bmp = (int)ini_getl("Settings", "init-bmp", 1, filename); state->probe = (int)ini_getl("Settings", "probe", 0, filename); @@ -355,12 +372,12 @@ static void profile_graph(struct nk_context *ctx, const char *id, APPSTATE *stat struct nk_user_font const *font = ctx->style.font; struct nk_style_window *stwin = &ctx->style.window; - nk_style_push_color(ctx, &stwin->fixed_background.data.color, nk_rgba(20, 29, 38, 225)); + nk_style_push_color(ctx, &stwin->fixed_background.data.color, COLOUR_BG0); if (nk_group_begin(ctx, id, widget_flags)) { if (tracelog_getstatusmsg(0) != NULL) { const char *text; for (int idx = 0; (text = tracelog_getstatusmsg(idx)) != NULL; idx++) { - struct nk_color clr = nk_rgb(255, 255, 128); + struct nk_color clr = COLOUR_FG_YELLOW; nk_layout_row_dynamic(ctx, rowheight, 1); nk_label_colored(ctx, text, NK_TEXT_LEFT, clr); } @@ -378,7 +395,7 @@ static void profile_graph(struct nk_context *ctx, const char *id, APPSTATE *stat struct nk_rect rc = nk_widget_bounds(ctx); assert(state->functionlist[fidx].ratio >= 0.0 && state->functionlist[fidx].ratio <= 1.0); rc.w *= state->functionlist[fidx].ratio; - nk_fill_rect(&win->buffer, rc, 0.0f, nk_rgb(127, 23, 45)); + nk_fill_rect(&win->buffer, rc, 0.0f, COLOUR_BG_YELLOW); nk_label(ctx, state->functionlist[fidx].percentage, NK_TEXT_RIGHT); /* print function name (get the width for the text first) */ const char *name = state->functionlist[fidx].name; @@ -402,7 +419,7 @@ static void profile_graph(struct nk_context *ctx, const char *id, APPSTATE *stat nk_layout_row_push(ctx, graphwidth); struct nk_rect rc = nk_widget_bounds(ctx); rc.w *= state->sourcelines[idx].ratio; - nk_fill_rect(&win->buffer, rc, 0.0f, nk_rgb(127, 23, 45)); + nk_fill_rect(&win->buffer, rc, 0.0f, COLOUR_BG_YELLOW); nk_label(ctx, state->sourcelines[idx].percentage, NK_TEXT_RIGHT); /* print source line (get the width for the text first) */ const char *text = state->sourcelines[idx].text; @@ -857,7 +874,10 @@ static bool collect_functions(APPSTATE *state) unsigned count = state->numfunctions - (pos + 1); memmove(&state->functionlist[pos + 1], &state->functionlist[pos], count * sizeof(FUNCTIONINFO)); } - state->functionlist[pos].name = strdup(elf_list[elf_idx].name); + char plain[256]; + if (!demangle(plain, sizearray(plain), elf_list[elf_idx].name)) + strlcpy(plain, elf_list[elf_idx].name, sizearray(plain)); + state->functionlist[pos].name = strdup(plain); state->functionlist[pos].addr_low = elf_list[elf_idx].address; state->functionlist[pos].addr_high = elf_list[elf_idx].address + elf_list[elf_idx].size; state->functionlist[pos].line_low = 0; @@ -1000,7 +1020,7 @@ static void panel_options(struct nk_context *ctx, APPSTATE *state, nk_label(ctx, "ELF file", NK_TEXT_ALIGN_LEFT | NK_TEXT_ALIGN_MIDDLE); nk_layout_row_push(ctx, VALUE_WIDTH - BROWSEBTN_WIDTH - 5); if (!state->dwarf_loaded) - nk_style_push_color(ctx,&ctx->style.edit.text_normal, nk_rgb(255, 80, 100)); + nk_style_push_color(ctx,&ctx->style.edit.text_normal, COLOUR_FG_RED); result = editctrl_tooltip(ctx, NK_EDIT_FIELD|NK_EDIT_SIG_ENTER|NK_EDIT_CLIPBOARD, state->ELFfile, sizearray(state->ELFfile), nk_filter_ascii, "ELF file for symbol lookup"); @@ -1012,15 +1032,14 @@ static void panel_options(struct nk_context *ctx, APPSTATE *state, } nk_layout_row_push(ctx, BROWSEBTN_WIDTH); if (nk_button_symbol(ctx, NK_SYMBOL_TRIPLE_DOT)) { - const char *s = noc_file_dialog_open(NOC_FILE_DIALOG_OPEN, - "ELF Executables\0*.elf;*.bin;*.\0All files\0*.*\0", - NULL, state->ELFfile, "Select ELF Executable", - guidriver_apphandle()); - if (s != NULL && strlen(s) < sizearray(state->ELFfile)) { - strcpy(state->ELFfile, s); + int res = noc_file_dialog_open(state->ELFfile, sizearray(state->ELFfile), + NOC_FILE_DIALOG_OPEN, + "ELF Executables\0*.elf;*.bin;*.\0All files\0*.*\0", + NULL, state->ELFfile, "Select ELF Executable", + guidriver_apphandle()); + if (res) { state->dwarf_loaded = false; state->curstate = STATE_LOAD_DWARF; - free((void*)s); } } nk_layout_row_end(ctx); @@ -1145,12 +1164,15 @@ static void button_bar(struct nk_context *ctx, APPSTATE *state) } nk_spacing(ctx, 1); if (nk_button_label(ctx, "Save") || nk_input_is_key_pressed(&ctx->input, NK_KEY_SAVE)) { - const char *s = noc_file_dialog_open(NOC_FILE_DIALOG_SAVE, - "CSV files\0*.csv\0All files\0*.*\0", - NULL, NULL, NULL, guidriver_apphandle()); - if (s != NULL) { - profile_save(s, state); - free((void*)s); + char path[_MAX_PATH]; + int res = noc_file_dialog_open(path, sizearray(path), NOC_FILE_DIALOG_SAVE, + "CSV files\0*.csv\0All files\0*.*\0", + NULL, NULL, NULL, guidriver_apphandle()); + if (res) { + const char *ext; + if ((ext = strrchr(path, '.')) == NULL || strchr(ext, DIRSEP_CHAR) != NULL) + strlcat(path, ".csv", sizearray(path)); /* default extension .csv */ + profile_save(path, state); } } } @@ -1226,11 +1248,10 @@ static void handle_stateaction(APPSTATE *state) /* allocate memory for sample map */ unsigned count = (state->code_top - state->code_base) / ADDRESS_ALIGN + 1; /* +1 for out-of-range samples */ state->sample_map = (unsigned*)malloc(count * sizeof(unsigned)); - if (state->sample_map != NULL) { + if (state->sample_map != NULL) memset(state->sample_map, 0, count * sizeof(unsigned)); - } else { + else tracelog_statusmsg(TRACESTATMSG_BMP, "Memory allocation error.", BMPSTAT_NOTICE); - } /* load dwarf */ int address_size; if (dwarf_read(fp, &dwarf_linetable, &dwarf_symboltable, &dwarf_filetable, &address_size)) @@ -1251,6 +1272,27 @@ static void handle_stateaction(APPSTATE *state) state->bitrate = strtoul(state->bitrate_str, NULL, 10); state->samplingfreq = strtoul(state->samplingfreq_str, NULL, 10); state->refreshrate = strtod(state->refreshrate_str, NULL); + int errcount = 0; + if (state->mcuclock < 1000) { + tracelog_statusmsg(TRACESTATMSG_BMP, "CPU clock frequency not set (or invalid).", BMPSTAT_NOTICE); + errcount += 1; + } + if (state->bitrate < 100) { + tracelog_statusmsg(TRACESTATMSG_BMP, "Bit rate (SWO) not set (or invalid).", BMPSTAT_NOTICE); + errcount += 1; + } + if (state->samplingfreq < 10) { + tracelog_statusmsg(TRACESTATMSG_BMP, "Sampling rate not set (or invalid).", BMPSTAT_NOTICE); + errcount += 1; + } + if (state->refreshrate < 0.001) { + tracelog_statusmsg(TRACESTATMSG_BMP, "Refresh interval not set (or invalid).", BMPSTAT_NOTICE); + errcount += 1; + } + if (errcount > 0) { + state->curstate = STATE_IDLE; + break; + } unsigned long params[4]; /* check to get more specific information on the MCU (specifically to update the mcu_family name, so that the appropriate scripts can be @@ -1279,12 +1321,17 @@ static void handle_stateaction(APPSTATE *state) params[1] = state->mcuclock / swvclock - 1; params[2] = divider - 1; bmp_runscript("swo_profile", state->mcu_family, state->mcu_architecture, params, 3); + state->init_done = true; } tracelog_statusmsg(TRACESTATMSG_BMP, "Starting profiling run...", BMPSTAT_SUCCESS); state->curstate = STATE_RUN; break; case STATE_RUN: tracelog_statusclear(); + if (state->init_target && !state->init_done) { + state->curstate = STATE_INIT_TARGET; + break; + } profile_reset(state, true); state->trace_status = trace_init(state->trace_endpoint, (state->probe == state->netprobe) ? state->IPaddr : NULL); state->curstate = (state->trace_status == TRACESTAT_OK) ? STATE_RUNNING : STATE_IDLE; @@ -1354,7 +1401,7 @@ int main(int argc, char *argv[]) case '?': case 'h': usage(NULL); - return 0; + return EXIT_SUCCESS; case 'f': ptr = &argv[idx][2]; if (*ptr == '=' || *ptr == ':') @@ -1373,6 +1420,9 @@ int main(int argc, char *argv[]) strlcpy(opt_fontmono, mono, sizearray(opt_fontmono)); } break; + case 'v': + version(); + return EXIT_SUCCESS; default: usage(argv[idx]); return EXIT_FAILURE; @@ -1477,14 +1527,16 @@ int main(int argc, char *argv[]) pointer_setstyle(CURSOR_NORMAL); else if (splitter_hor.hover) pointer_setstyle(CURSOR_LEFTRIGHT); +#if defined __linux__ else pointer_setstyle(CURSOR_NORMAL); +#endif } nk_end(ctx); /* Draw */ - guidriver_render(nk_rgb(30,30,30)); + guidriver_render(COLOUR_BG0_S); } save_settings(txtConfigFile, &appstate, tab_states, &splitter_hor); diff --git a/source/bmprofile.rc b/source/bmprofile.rc index 6e6f59a..40bf4be 100644 --- a/source/bmprofile.rc +++ b/source/bmprofile.rc @@ -20,6 +20,7 @@ #include #include +#include "svnrev.h" AppIcon ICON "res/icon_profile.ico" @@ -29,9 +30,9 @@ AppIcon ICON "res/icon_profile.ico" * for details on version information and the VERSIONINFO structure. */ #define VERSION 1 -#define REVISION 0 -#define BUILD 0 -#define VERSIONSTR "1.0.0\0" +#define REVISION 1 +#define BUILD SVNREV_NUM +#define VERSIONSTR "1.0.*\0" #define VERSIONNAME "bmprofile.exe\0" #define VERSIONDESCRIPTION "Statistical Profiler\0" #define VERSIONCOMPANYNAME "CompuPhase\0" diff --git a/source/bmscan.c b/source/bmscan.c index 4224699..c3d122f 100644 --- a/source/bmscan.c +++ b/source/bmscan.c @@ -4,10 +4,7 @@ * it scans the registry for the Black Magic Probe device, under Linux, it * browses through sysfs. * - * Build this file with the macro STANDALONE defined on the command line to - * create a self-contained executable. - * - * Copyright 2019-2021 CompuPhase + * Copyright 2019-2022 CompuPhase * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,6 +29,7 @@ #endif #include "bmp-scan.h" #include "tcpip.h" +#include "svnrev.h" #if defined _MSC_VER @@ -103,8 +101,9 @@ int main(int argc, char *argv[]) int print_all = 1; if (argc >= 2 && (argv[1][0] == '-' || argv[1][0] == '/' || argv[1][0] == '?')) { - printf("bmscan detects the ports used by Black Magic Probe devices that are connected.\n\n" - "There are two options:\n" + printf("BMScan detects which ports a Black Magic Probe is connected to. If multiple\n" + "probes are connected, it can list them all.\n\n" + "There are two options on the command line:\n" "* The sequence number of the Black Magic Probe (if multiple are connected).\n" " Alternatively, you may specify the serial number of the Black Magic Probe, in\n" " hexadecimal.\n" @@ -118,8 +117,11 @@ int main(int argc, char *argv[]) " bmscan gdbserver - list the COM-port / tty device for GDB-server of\n" " the first device.\n" " bmscan 2 swo - list the GUID / device for the SWO trace output\n" - " for the second device\n"); - return 0; + " for the second device\n" + " bmscan ip - list all IP addresses on which a ctxLink probe\n" + " is detected.\n\n" + "Version 1.1.%d, Copyright 2019-2022 CompuPhase.\nLicensed under the Apache License version 2.0.\n", SVNREV_NUM); + return EXIT_SUCCESS; } /* check command line arguments */ @@ -148,13 +150,13 @@ int main(int argc, char *argv[]) seqnr = idx; if (seqnr == -1) { printf("\nBlack Magic Probe with serial number %s is not found.\n", serial); - return 1; + return EXIT_FAILURE; } } if (seqnr < 0) { printf("\nInvalid sequence number %ld, sequence numbers start at 1.\n", seqnr + 1); - return 1; + return EXIT_FAILURE; } if (iface != NULL) { @@ -182,7 +184,7 @@ int main(int argc, char *argv[]) int result = tcpip_init(); if (result != 0) { printf("network initialization failure (error code %d)\n", result); - return 1; + return EXIT_FAILURE; } count = scan_network(addresses, sizearray(addresses)); if (seqnr == 0) { @@ -228,7 +230,7 @@ int main(int argc, char *argv[]) default: printf("\nNo %ldth Black Magic Probe could be found on this system.\n", seqnr + 1); } - return 1; + return EXIT_FAILURE; } if (find_bmp(seqnr, BMP_IF_UART, port_term, sizearray(port_term))) { @@ -250,6 +252,6 @@ int main(int argc, char *argv[]) } while (print_all); } - return 0; + return EXIT_SUCCESS; } diff --git a/source/bmtrace.c b/source/bmtrace.c index b620838..c6d1b7e 100644 --- a/source/bmtrace.c +++ b/source/bmtrace.c @@ -65,6 +65,7 @@ #include "rs232.h" #include "specialfolder.h" #include "tcpip.h" +#include "svnrev.h" #include "parsetsdl.h" #include "decodectf.h" @@ -92,15 +93,17 @@ #endif #if defined WIN32 || defined _WIN32 + #define DIRSEP_CHAR '\\' #define IS_OPTION(s) ((s)[0] == '-' || (s)[0] == '/') #else + #define DIRSEP_CHAR '/' #define IS_OPTION(s) ((s)[0] == '-') #endif static DWARF_LINELOOKUP dwarf_linetable = { NULL }; -static DWARF_SYMBOLLIST dwarf_symboltable = { NULL}; -static DWARF_PATHLIST dwarf_filetable = { NULL}; +static DWARF_SYMBOLLIST dwarf_symboltable = { NULL }; +static DWARF_PATHLIST dwarf_filetable = { NULL }; #define WINDOW_WIDTH 700 /* default window size (window is resizable) */ #define WINDOW_HEIGHT 400 @@ -156,7 +159,22 @@ static void usage(const char *invalid_option) "Options:\n" "-f=value Font size to use (value must be 8 or larger).\n" "-h This help.\n" - "-t=path Path to the TSDL metadata file to use.\n"); + "-t=path Path to the TSDL metadata file to use.\n" + "-v Show version information.\n"); +} + +static void version(void) +{ + #if defined _WIN32 /* fix console output on Windows */ + if (AttachConsole(ATTACH_PARENT_PROCESS)) { + freopen("CONOUT$", "wb", stdout); + freopen("CONOUT$", "wb", stderr); + } + printf("\n"); + #endif + + printf("BMTrace version 1.1.%d.\n", SVNREV_NUM); + printf("Copyright 2019-2022 CompuPhase\nLicensed under the Apache License version 2.0\n"); } typedef struct tagAPPSTATE { @@ -177,12 +195,15 @@ typedef struct tagAPPSTATE { int init_target; /**< whether to configure the target MCU for tracing */ int init_bmp; /**< whether to configure the debug probe for tracing */ int connect_srst; /**< whether to force reset while attaching */ + int live_view; /**< live view while running (default = on) */ + unsigned long trace_count; /**< accumulated number of captured trace messages */ char cpuclock_str[16]; /**< edit buffer for CPU clock frequency */ unsigned long mcuclock; /**< active CPU clock frequency */ char bitrate_str[16]; /**< edit buffer for bitrate */ unsigned long bitrate; /**< active bitrate */ int datasize; /**< packet size */ bool reload_format; /**< whether to reload the TSDL file */ + bool clear_channels; /**< whether to reset all channels to default */ char TSDLfile[_MAX_PATH]; /**< CTF decoding, message file */ char ELFfile[_MAX_PATH]; /**< ELF file for symbol/address look-up */ TRACEFILTER *filterlist; /**< filter expressions */ @@ -199,8 +220,9 @@ typedef struct tagAPPSTATE { enum { TAB_CONFIGURATION, - TAB_CHANNELS, + TAB_STATUS, TAB_FILTERS, + TAB_CHANNELS, /* --- */ TAB_COUNT }; @@ -214,11 +236,12 @@ static bool save_settings(const char *filename, const APPSTATE *state, const enum nk_collapse_states tab_states[], const SPLITTERBAR *splitter_hor, const SPLITTERBAR *splitter_ver) { + assert(filename != NULL); assert(state != NULL); assert(tab_states != NULL); assert(splitter_hor != NULL && splitter_ver != NULL); - if (filename == NULL || strlen(filename) == 0) + if (strlen(filename) == 0) return false; char valstr[128]; @@ -254,6 +277,7 @@ static bool save_settings(const char *filename, const APPSTATE *state, ini_putl("Settings", "init-target", state->init_target, filename); ini_putl("Settings", "init-bmp", state->init_bmp, filename); ini_putl("Settings", "connect-srst", state->connect_srst, filename); + ini_putl("Settings", "live-view", state->live_view, filename); ini_putl("Settings", "datasize", state->datasize, filename); ini_puts("Settings", "tsdl", state->TSDLfile, filename); ini_puts("Settings", "elf", state->ELFfile, filename); @@ -277,20 +301,18 @@ static bool load_settings(const char *filename, APPSTATE *state, enum nk_collapse_states tab_states[], SPLITTERBAR *splitter_hor, SPLITTERBAR *splitter_ver) { + assert(filename != NULL); assert(state != NULL); assert(tab_states != NULL); assert(splitter_hor != NULL && splitter_ver != NULL); - if (filename == NULL || strlen(filename) == 0 || access(filename, 0) != 0) - return false; - /* read channel configuration */ char valstr[128]; for (int chan = 0; chan < NUM_CHANNELS; chan++) { char key[41]; unsigned clr; int enabled, result; - channel_set(chan, (chan == 0), NULL, nk_rgb(190, 190, 190)); /* preset: port 0 is enabled by default, others disabled by default */ + channel_set(chan, (chan == 0), NULL, SWO_TRACE_DEFAULT_COLOR); /* preset: port 0 is enabled by default, others disabled by default */ sprintf(key, "chan%d", chan); ini_gets("Channels", key, "", valstr, sizearray(valstr), filename); result = sscanf(valstr, "%d #%x %40s", &enabled, &clr, key); @@ -335,6 +357,7 @@ static bool load_settings(const char *filename, APPSTATE *state, state->init_bmp = 0; } state->connect_srst = (int)ini_getl("Settings", "connect-srst", 0, filename); + state->live_view = (int)ini_getl("Settings", "live-view", 1, filename); state->datasize = (int)ini_getl("Settings", "datasize", 1, filename); ini_gets("Settings", "tsdl", "", state->TSDLfile, sizearray(state->TSDLfile), filename); ini_gets("Settings", "elf", "", state->ELFfile, sizearray(state->ELFfile), filename); @@ -359,7 +382,7 @@ static bool load_settings(const char *filename, APPSTATE *state, for (int idx = 0; idx < TAB_COUNT; idx++) { char key[40]; int opened, result; - tab_states[idx] = (idx == TAB_CONFIGURATION) ? NK_MAXIMIZED : NK_MINIMIZED; + tab_states[idx] = (idx == TAB_CONFIGURATION || idx == TAB_STATUS) ? NK_MAXIMIZED : NK_MINIMIZED; sprintf(key, "view%d", idx); ini_gets("Settings", key, "", valstr, sizearray(valstr), filename); result = sscanf(valstr, "%d", &opened); @@ -388,7 +411,7 @@ static void find_popup(struct nk_context *ctx, APPSTATE *state, float canvas_wid nk_layout_row(ctx, NK_DYNAMIC, opt_fontsize, 2, nk_ratio(2, 0.2, 0.8)); nk_spacing(ctx, 1); if (state->find_popup == 2) - nk_label_colored(ctx, "Text not found", NK_TEXT_ALIGN_LEFT | NK_TEXT_ALIGN_MIDDLE, nk_rgb(255, 80, 100)); + nk_label_colored(ctx, "Text not found", NK_TEXT_ALIGN_LEFT | NK_TEXT_ALIGN_MIDDLE, COLOUR_FG_RED); nk_layout_row_dynamic(ctx, ROW_HEIGHT, 3); nk_spacing(ctx, 1); if (nk_button_label(ctx, "Find") || nk_input_is_key_pressed(&ctx->input, NK_KEY_ENTER)) { @@ -528,6 +551,9 @@ static void panel_options(struct nk_context *ctx, APPSTATE *state, if (state->datasize != result) { trace_setdatasize((state->datasize == 3) ? 4 : (short)state->datasize); tracestring_clear(); + trace_overflowerrors(true); + ctf_decode_reset(); + state->trace_count = 0; if (state->trace_status == TRACESTAT_OK) tracelog_statusmsg(TRACESTATMSG_BMP, "Listening ...", BMPSTAT_SUCCESS); } @@ -538,24 +564,26 @@ static void panel_options(struct nk_context *ctx, APPSTATE *state, nk_label(ctx, "TSDL file", NK_TEXT_ALIGN_LEFT | NK_TEXT_ALIGN_MIDDLE); nk_layout_row_push(ctx, VALUE_WIDTH - BROWSEBTN_WIDTH - 5); if (state->error_flags & ERROR_NO_TSDL) - nk_style_push_color(ctx,&ctx->style.edit.text_normal, nk_rgb(255, 80, 100)); + nk_style_push_color(ctx,&ctx->style.edit.text_normal, COLOUR_FG_RED); result = editctrl_tooltip(ctx, NK_EDIT_FIELD|NK_EDIT_SIG_ENTER|NK_EDIT_CLIPBOARD, state->TSDLfile, sizearray(state->TSDLfile), nk_filter_ascii, "Metadata file for Common Trace Format (CTF)"); - if (result & (NK_EDIT_COMMITED | NK_EDIT_DEACTIVATED)) + if (result & (NK_EDIT_COMMITED | NK_EDIT_DEACTIVATED)) { + state->clear_channels = true; state->reload_format = true; + } if (state->error_flags & ERROR_NO_TSDL) nk_style_pop_color(ctx); nk_layout_row_push(ctx, BROWSEBTN_WIDTH); if (nk_button_symbol(ctx, NK_SYMBOL_TRIPLE_DOT)) { - const char *s = noc_file_dialog_open(NOC_FILE_DIALOG_OPEN, - "TSDL files\0*.tsdl;*.ctf\0All files\0*.*\0", - NULL, state->TSDLfile, "Select metadata file for CTF", - guidriver_apphandle()); - if (s != NULL && strlen(s) < sizearray(state->TSDLfile)) { - strcpy(state->TSDLfile, s); + int res = noc_file_dialog_open(state->TSDLfile, sizearray(state->TSDLfile), + NOC_FILE_DIALOG_OPEN, + "TSDL files\0*.tsdl;*.ctf\0All files\0*.*\0", + NULL, state->TSDLfile, "Select metadata file for CTF", + guidriver_apphandle()); + if (res) { + state->clear_channels = true; state->reload_format = true; - free((void*)s); } } nk_layout_row_end(ctx); @@ -564,7 +592,7 @@ static void panel_options(struct nk_context *ctx, APPSTATE *state, nk_label(ctx, "ELF file", NK_TEXT_ALIGN_LEFT | NK_TEXT_ALIGN_MIDDLE); nk_layout_row_push(ctx, VALUE_WIDTH - BROWSEBTN_WIDTH - 5); if (state->error_flags & ERROR_NO_ELF) - nk_style_push_color(ctx,&ctx->style.edit.text_normal, nk_rgb(255, 80, 100)); + nk_style_push_color(ctx,&ctx->style.edit.text_normal, COLOUR_FG_RED); result = editctrl_tooltip(ctx, NK_EDIT_FIELD|NK_EDIT_SIG_ENTER|NK_EDIT_CLIPBOARD, state->ELFfile, sizearray(state->ELFfile), nk_filter_ascii, "ELF file for symbol lookup"); @@ -574,19 +602,54 @@ static void panel_options(struct nk_context *ctx, APPSTATE *state, nk_style_pop_color(ctx); nk_layout_row_push(ctx, BROWSEBTN_WIDTH); if (nk_button_symbol(ctx, NK_SYMBOL_TRIPLE_DOT)) { - const char *s = noc_file_dialog_open(NOC_FILE_DIALOG_OPEN, - "ELF Executables\0*.elf;*.bin;*.\0All files\0*.*\0", - NULL, state->ELFfile, "Select ELF Executable", - guidriver_apphandle()); - if (s != NULL && strlen(s) < sizearray(state->ELFfile)) { - strcpy(state->ELFfile, s); + int res = noc_file_dialog_open(state->ELFfile, sizearray(state->ELFfile), + NOC_FILE_DIALOG_OPEN, + "ELF Executables\0*.elf;*.bin;*.\0All files\0*.*\0", + NULL, state->ELFfile, "Select ELF Executable", + guidriver_apphandle()); + if (res) state->reload_format = true; - free((void*)s); - } } nk_layout_row_end(ctx); + + nk_layout_row_dynamic(ctx, ROW_HEIGHT, 1); + checkbox_tooltip(ctx, "Live view of captured traces", &state->live_view, NK_TEXT_LEFT, "See trace messages as they arrive.\nIf disabled, stop the trace capture to view the messages."); + + nk_tree_state_pop(ctx); + } + #undef LABEL_WIDTH + #undef VALUE_WIDTH +} + +static void panel_status(struct nk_context *ctx, APPSTATE *state, + enum nk_collapse_states tab_states[TAB_COUNT], + float panel_width) +{ + #define LABEL_WIDTH(n) ((n) * opt_fontsize) + #define VALUE_WIDTH(n) (panel_width - LABEL_WIDTH(n) - 26) + + if (nk_tree_state_push(ctx, NK_TREE_TAB, "Status", &tab_states[TAB_STATUS])) { + char valuestr[20]; + nk_layout_row_begin(ctx, NK_STATIC, ROW_HEIGHT, 2); + nk_layout_row_push(ctx, LABEL_WIDTH(8)); + nk_label(ctx, "Overflow events", NK_TEXT_ALIGN_LEFT | NK_TEXT_ALIGN_MIDDLE); + nk_layout_row_push(ctx, VALUE_WIDTH(8)); + sprintf(valuestr, "%u", trace_overflowerrors(false)); + label_tooltip(ctx, valuestr, NK_TEXT_ALIGN_LEFT | NK_TEXT_ALIGN_MIDDLE, "Overflow event count.\nDisable 'Live view' to avoid queue overflows."); + nk_layout_row_end(ctx); + + nk_layout_row_begin(ctx, NK_STATIC, ROW_HEIGHT, 2); + nk_layout_row_push(ctx, LABEL_WIDTH(8)); + nk_label(ctx, "Packet errors", NK_TEXT_ALIGN_LEFT | NK_TEXT_ALIGN_MIDDLE); + nk_layout_row_push(ctx, VALUE_WIDTH(8)); + sprintf(valuestr, "%u", trace_getpacketerrors(false)); + label_tooltip(ctx, valuestr, NK_TEXT_ALIGN_LEFT | NK_TEXT_ALIGN_MIDDLE, "SWO packet errors.\nVerify 'Data size' setting."); + nk_layout_row_end(ctx); + nk_tree_state_pop(ctx); } + #undef LABEL_WIDTH + #undef VALUE_WIDTH } static void filter_options(struct nk_context *ctx, APPSTATE *state, @@ -689,6 +752,7 @@ static void channel_options(struct nk_context *ctx, APPSTATE *state, enabled = channel_getenabled(chan); if (checkbox_tooltip(ctx, label, &enabled, NK_TEXT_LEFT, "Enable/disable this channel")) { /* enable/disable channel in the target */ + pointer_setstyle(CURSOR_WAIT); channel_setenabled(chan, enabled); if (state->init_target) { if (enabled) @@ -703,9 +767,10 @@ static void channel_options(struct nk_context *ctx, APPSTATE *state, bmp_runscript("swo_channels", state->mcu_family, state->mcu_architecture, params, 2); } } + pointer_setstyle(CURSOR_NORMAL); } clrbk = channel_getcolor(chan); - clrtxt = (clrbk.r + 2 * clrbk.g + clrbk.b < 700) ? nk_rgb(255,255,255) : nk_rgb(20,29,38); + clrtxt = (clrbk.r + 2 * clrbk.g + clrbk.b < 700) ? COLOUR_HIGHLIGHT : COLOUR_BG0; stbtn.normal.data.color = stbtn.hover.data.color = stbtn.active.data.color = stbtn.text_background = clrbk; stbtn.text_normal = stbtn.text_active = stbtn.text_hover = clrtxt; @@ -760,9 +825,18 @@ static void channel_options(struct nk_context *ctx, APPSTATE *state, static void button_bar(struct nk_context *ctx, APPSTATE *state) { nk_layout_row(ctx, NK_DYNAMIC, ROW_HEIGHT, 7, nk_ratio(7, 0.19, 0.08, 0.19, 0.08, 0.19, 0.08, 0.19)); - const char *ptr = state->trace_running ? "Stop" : tracestring_isempty() ? "Start" : "Resume"; - if (nk_button_label(ctx, ptr) || nk_input_is_key_pressed(&ctx->input, NK_KEY_F5)) { + const char *caption; + if (state->trace_running) { + caption = "Stop"; + } else if (tracestring_isempty()) { + caption = "Start"; + state->trace_count = 0; + } else { + caption = "Resume"; + } + if (nk_button_label(ctx, caption) || nk_input_is_key_pressed(&ctx->input, NK_KEY_F5)) { state->trace_running = !state->trace_running; + trace_overflowerrors(true); if (state->trace_running && state->trace_status != TRACESTAT_OK) { state->trace_status = trace_init(state->trace_endpoint, (state->probe == state->netprobe) ? state->IPaddr : NULL); if (state->trace_status != TRACESTAT_OK) @@ -772,6 +846,9 @@ static void button_bar(struct nk_context *ctx, APPSTATE *state) nk_spacing(ctx, 1); if (nk_button_label(ctx, "Clear")) { tracestring_clear(); + trace_overflowerrors(true); + ctf_decode_reset(); + state->trace_count = 0; state->cur_match_line = -1; } nk_spacing(ctx, 1); @@ -779,16 +856,44 @@ static void button_bar(struct nk_context *ctx, APPSTATE *state) state->find_popup = 1; nk_spacing(ctx, 1); if (nk_button_label(ctx, "Save") || nk_input_is_key_pressed(&ctx->input, NK_KEY_SAVE)) { - const char *s = noc_file_dialog_open(NOC_FILE_DIALOG_SAVE, - "CSV files\0*.csv\0All files\0*.*\0", - NULL, NULL, NULL, guidriver_apphandle()); - if (s != NULL) { - tracestring_save(s); - free((void*)s); + char path[_MAX_PATH]; + int res = noc_file_dialog_open(path, sizearray(path), NOC_FILE_DIALOG_SAVE, + "CSV files\0*.csv\0All files\0*.*\0", + NULL, NULL, NULL, guidriver_apphandle()); + if (res) { + const char *ext; + if ((ext = strrchr(path, '.')) == NULL || strchr(ext, DIRSEP_CHAR) != NULL) + strlcat(path, ".csv", sizearray(path)); /* default extension .csv */ + tracestring_save(path); } } } +void tracelog_runcount(struct nk_context *ctx, const char *id, int rowheight, unsigned long capture_count, nk_flags widget_flags) +{ + /* (near) black background on group */ + struct nk_style_window *stwin = &ctx->style.window; + nk_style_push_color(ctx, &stwin->fixed_background.data.color, COLOUR_BG0); + + struct nk_rect rcwidget = nk_layout_widget_bounds(ctx); + if (nk_group_begin(ctx, id, widget_flags)) { + nk_layout_row_dynamic(ctx, rcwidget.h/5, 1); + nk_spacing(ctx, 1); + + struct nk_color clr = COLOUR_FG_AQUA; + nk_layout_row_dynamic(ctx, rowheight, 1); + nk_label_colored(ctx, "Running... (click 'Stop' to view captured messages, or enable 'Live view')", NK_TEXT_CENTERED, clr); + if (capture_count != ~0) { + char msg[100]; + sprintf(msg, "captured %lu messages", capture_count); + nk_label_colored(ctx, msg, NK_TEXT_CENTERED, clr); + } + + nk_group_end(ctx); + } + nk_style_pop_color(ctx); +} + static void handle_stateaction(APPSTATE *state) { if (state->reinitialize == 1) { @@ -796,6 +901,9 @@ static void handle_stateaction(APPSTATE *state) char msg[100]; tracelog_statusclear(); tracestring_clear(); + trace_overflowerrors(true); + ctf_decode_reset(); + state->trace_count = 0; if ((state->mcuclock = strtol(state->cpuclock_str, NULL, 10)) == 0) state->mcuclock = 48000000; if (state->swomode == MODE_MANCHESTER || (state->bitrate = strtol(state->bitrate_str, NULL, 10)) == 0) @@ -918,19 +1026,29 @@ static void handle_stateaction(APPSTATE *state) ctf_parse_cleanup(); ctf_decode_cleanup(); tracestring_clear(); + trace_overflowerrors(true); + ctf_decode_reset(); dwarf_cleanup(&dwarf_linetable, &dwarf_symboltable, &dwarf_filetable); state->cur_match_line = -1; state->error_flags = 0; + state->trace_count = 0; if (strlen(state->TSDLfile) > 0) state->error_flags |= ERROR_NO_TSDL; - if (strlen(state->TSDLfile)> 0 && access(state->TSDLfile, 0)== 0) { + if (strlen(state->TSDLfile)> 0 && access(state->TSDLfile, 0) == 0) { if (ctf_parse_init(state->TSDLfile) && ctf_parse_run()) { - const CTF_STREAM *stream; - int seqnr; + /* optionally clear all channel names & colours */ + if (state->clear_channels) + for (int chan = 0; chan < NUM_CHANNELS; chan++) + channel_set(chan, false, NULL, SWO_TRACE_DEFAULT_COLOR); /* stream names overrule configured channel names */ - for (seqnr = 0; (stream = stream_by_seqnr(seqnr)) != NULL; seqnr++) - if (stream->name != NULL && strlen(stream->name) > 0) - channel_setname(seqnr, stream->name); + const CTF_STREAM *stream; + for (int seqnr = 0; (stream = stream_by_seqnr(seqnr)) != NULL; seqnr++) { + if (stream->name != NULL && strlen(stream->name) > 0) { + int chan = stream->stream_id; + assert(chan >= 0 && chan < NUM_CHANNELS); + channel_set(chan, true, stream->name, SWO_TRACE_DEFAULT_COLOR); + } + } state->error_flags &= ~ERROR_NO_TSDL; tracelog_statusmsg(TRACESTATMSG_CTF, "CTF mode active", BMPSTAT_SUCCESS); } else { @@ -945,6 +1063,7 @@ static void handle_stateaction(APPSTATE *state) int address_size; dwarf_read(fp, &dwarf_linetable, &dwarf_symboltable, &dwarf_filetable, &address_size); fclose(fp); + ctf_set_symtable(&dwarf_symboltable); state->error_flags &= ~ERROR_NO_ELF; } } @@ -1002,7 +1121,7 @@ int main(int argc, char *argv[]) case '?': case 'h': usage(NULL); - return 0; + return EXIT_SUCCESS; case 'f': ptr = &argv[idx][2]; if (*ptr == '=' || *ptr == ':') @@ -1028,6 +1147,9 @@ int main(int argc, char *argv[]) if (access(ptr, 0) == 0) strlcpy(appstate.TSDLfile, ptr, sizearray(appstate.TSDLfile)); break; + case 'v': + version(); + return EXIT_SUCCESS; default: usage(argv[idx]); return EXIT_FAILURE; @@ -1048,7 +1170,7 @@ int main(int argc, char *argv[]) *ext = '\0'; strlcat(appstate.TSDLfile, ".tsdl", sizearray(appstate.TSDLfile)); if (access(appstate.TSDLfile, 0) != 0) - appstate.TSDLfile[0] = '\0'; /* newly constructed file not found, clear name */ + appstate.TSDLfile[0] = '\0'; /* presumed TSDL file not found, clear name */ } } fclose(fp); @@ -1094,22 +1216,26 @@ int main(int argc, char *argv[]) /* left column */ if (nk_group_begin(ctx, "left", NK_WINDOW_NO_SCROLLBAR)) { /* trace log */ - if (appstate.trace_status == TRACESTAT_OK && tracestring_isempty() && trace_getpacketerrors() > 0) { - char msg[100]; - sprintf(msg, "SWO packet errors (%d), verify data size", trace_getpacketerrors()); - tracelog_statusmsg(TRACESTATMSG_BMP, msg, BMPERR_GENERAL); - } - waitidle = tracestring_process(appstate.trace_running) == 0; + int count = tracestring_process(appstate.trace_running); + appstate.trace_count += count; + waitidle = (count == 0); nk_layout_row_dynamic(ctx, nk_vsplitter_rowheight(&splitter_ver, 0), 1); - tracelog_widget(ctx, "tracelog", opt_fontsize, appstate.cur_match_line, appstate.filterlist, NK_WINDOW_BORDER); + if (appstate.trace_running && !appstate.live_view) + tracelog_runcount(ctx, "runcount", opt_fontsize, appstate.trace_count, NK_WINDOW_BORDER); + else + tracelog_widget(ctx, "tracelog", opt_fontsize, appstate.cur_match_line, appstate.filterlist, NK_WINDOW_BORDER); /* vertical splitter */ nk_vsplitter(ctx, &splitter_ver); /* timeline & button bar */ nk_layout_row_dynamic(ctx, nk_vsplitter_rowheight(&splitter_ver, 1), 1); - double click_time = timeline_widget(ctx, "timeline", opt_fontsize, NK_WINDOW_BORDER); - appstate.cur_match_line = (click_time >= 0.0) ? tracestring_findtimestamp(click_time) : -1; + if (appstate.trace_running && !appstate.live_view) { + tracelog_runcount(ctx, "voidtimeline", opt_fontsize, ~0, NK_WINDOW_BORDER); + } else { + double click_time = timeline_widget(ctx, "timeline", opt_fontsize, NK_WINDOW_BORDER); + appstate.cur_match_line = (click_time >= 0.0) ? tracestring_findtimestamp(click_time) : -1; + } nk_layout_row_dynamic(ctx, SPACING, 1); button_bar(ctx, &appstate); @@ -1123,6 +1249,7 @@ int main(int argc, char *argv[]) /* right column */ if (nk_group_begin(ctx, "right", NK_WINDOW_BORDER)) { panel_options(ctx, &appstate, tab_states, nk_hsplitter_colwidth(&splitter_hor, 1)); + panel_status(ctx, &appstate, tab_states, nk_hsplitter_colwidth(&splitter_hor, 1)); filter_options(ctx, &appstate, tab_states); channel_options(ctx, &appstate, tab_states); nk_group_end(ctx); @@ -1138,13 +1265,15 @@ int main(int argc, char *argv[]) pointer_setstyle(CURSOR_UPDOWN); else if (splitter_hor.hover) pointer_setstyle(CURSOR_LEFTRIGHT); +#if defined __linux__ else pointer_setstyle(CURSOR_NORMAL); +#endif } nk_end(ctx); /* Draw */ - guidriver_render(nk_rgb(30,30,30)); + guidriver_render(COLOUR_BG0_S); } /* save configuration */ diff --git a/source/bmtrace.rc b/source/bmtrace.rc index d5e4d21..dc18461 100644 --- a/source/bmtrace.rc +++ b/source/bmtrace.rc @@ -20,6 +20,7 @@ #include #include +#include "svnrev.h" AppIcon ICON "res/icon_trace.ico" @@ -29,9 +30,9 @@ AppIcon ICON "res/icon_trace.ico" * for details on version information and the VERSIONINFO structure. */ #define VERSION 1 -#define REVISION 0 -#define BUILD 0 -#define VERSIONSTR "1.0.0\0" +#define REVISION 1 +#define BUILD SVNREV_NUM +#define VERSIONSTR "1.1.*\0" #define VERSIONNAME "bmtrace.exe\0" #define VERSIONDESCRIPTION "Serial Wire Trace Viewer\0" #define VERSIONCOMPANYNAME "CompuPhase\0" diff --git a/source/calltree.c b/source/calltree.c new file mode 100644 index 0000000..faa544a --- /dev/null +++ b/source/calltree.c @@ -0,0 +1,376 @@ +/* + * Utility functions to create a calltree from a CSV file that BMTrace has + * generated from function function entry and function exit traces. + * + * Build this file with the macro STANDALONE defined on the command line to + * create a self-contained executable. + * + * Copyright 2022 CompuPhase + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include +#include +#include +#include "svnrev.h" + +#if defined __linux__ + #include +#elif defined __MINGW32__ || defined __MINGW64__ || defined _MSC_VER + #include "strlcpy.h" +#endif + +#if !defined _MAX_PATH + #define _MAX_PATH 260 +#endif + +#if !defined sizearray + #define sizearray(a) (sizeof(a) / sizeof((a)[0])) +#endif + +#if defined WIN32 || defined _WIN32 + #define IS_OPTION(s) ((s)[0] == '-' || (s)[0] == '/') +#else + #define IS_OPTION(s) ((s)[0] == '-') +#endif + +typedef struct tagFUNCDEF { + struct tagFUNCDEF *next; + char *name; + int count; + bool skip; + struct tagFUNCDEF *caller; + struct tagFUNCDEF *callees; +} FUNCDEF; + +enum { + TYPE_INVALID, + TYPE_ENTER, + TYPE_EXIT, +}; + +static FUNCDEF calltree = { NULL }; +static FUNCDEF *current = NULL; + + +static const char *skipwhite(const char *text) +{ + assert(text != NULL); + while (*text != '\0' && *text <= ' ') + text++; + return text; +} + +static const char *skiptodelim(const char *text, char delimiter) +{ + assert(text != NULL); + while (*text != '\0' && *text != delimiter) + text++; + return text; +} + +static int match_function(const char *line, int channel, char *name, size_t namesize, + const char *func_enter, const char *func_exit) +{ + assert(func_enter != NULL && strlen(func_enter) > 0); + assert(func_exit != NULL && strlen(func_exit) > 0); + size_t func_enter_len = strlen(func_enter); + size_t func_exit_len = strlen(func_exit); + + assert(line != NULL); + long seqnr = strtol(line, NULL, 10); + if (seqnr != channel) + return TYPE_INVALID; /* not the channel for channel trace */ + + const char *ptr = line; + /* skip channel number */ + ptr = skiptodelim(ptr, ','); + if (*ptr == ',') + ptr++; + /* skip channel name */ + ptr = skipwhite(ptr); + if (*ptr == '"') + ptr = skiptodelim(ptr + 1, '"'); + ptr = skiptodelim(ptr, ','); + if (*ptr == ',') + ptr++; + /* skip timestamp */ + ptr = skiptodelim(ptr, ','); + if (*ptr == ',') + ptr++; + /* check enter/exit code */ + if (*ptr == '"') + ptr = skipwhite(ptr + 1); + if (strncmp(ptr, func_enter, func_enter_len) != 0 && strncmp(ptr, func_exit, func_exit_len) != 0) + return TYPE_INVALID; /* required keyword not found */ + if (strchr(ptr, ':') == NULL || strchr(ptr, '=') == NULL) + return TYPE_INVALID; /* syntax not correct for function trace */ + + int result = (strncmp(ptr, func_enter, func_enter_len)== 0) ? TYPE_ENTER : TYPE_EXIT; + ptr = skiptodelim(ptr, '='); + assert(ptr != NULL); /* presence of '=' was already checked above */ + const char *fname = skipwhite(ptr + 1); + size_t len = ((ptr = strchr(fname, '"')) != NULL) ? ptr - fname : strlen(fname); + len += 1; /* add size of '\0' terminator */ + if (len > namesize) + len = namesize; + assert(name != NULL && namesize > 0); + strlcpy(name, fname, len); + + return result; +} + +static void enter_function(const char *name) +{ + assert(name != NULL); + + /* check whether this function is already among the callees of the current + function */ + FUNCDEF *localroot = (current != NULL) ? current->callees : &calltree; + assert(localroot != NULL); + FUNCDEF *entry; + for (entry = localroot->next; entry != NULL && strcmp(entry->name, name) != 0; entry = entry->next) + {} + if (entry != NULL) { + entry->count += 1; /* this function was already called at this level -> just increment count */ + current = entry; + return; + } + + /* add entry */ + entry = malloc(sizeof(FUNCDEF)); + if (entry != NULL) { + memset(entry, 0, sizeof(FUNCDEF)); + entry->callees = malloc(sizeof(FUNCDEF)); + entry->name = strdup(name); + if (entry->callees != NULL && entry->name != NULL) { + entry->count = 1; + entry->caller = current; + memset(entry->callees, 0, sizeof(FUNCDEF)); + FUNCDEF *pos = (current != NULL) ? current->callees : &calltree; + while (pos->next != NULL) + pos = pos->next; + pos->next = entry; + current = entry; + } else { + if (entry->callees != NULL) + free((void*)entry->callees); + if (entry->name != NULL) + free((void*)entry->name); + free((void*)entry); + } + } +} + +static void exit_function(const char *name) +{ + if (current != NULL) { + assert(current->name != NULL); + if (strcmp(current->name, name) != 0) + fprintf(stderr, "Warning: exit function '%s' does not match entry for '%s'.\n", name, current->name); + current = current->caller; + } else { + fprintf(stderr, "Warning: exit function '%s' at call stack level 0.\n", name); + } +} + +static void print_graph(FUNCDEF *root, int level) +{ + for (FUNCDEF *entry = root->next; entry != NULL; entry = entry->next) { + for (int indent = 0; indent < level; indent++) + printf(" "); + assert(entry->name != NULL); + printf("%s", entry->name); + if (entry->count > 1) + printf(" [%dx]", entry->count); + printf("\n"); + print_graph(entry->callees, level + 1); + } +} + +static FUNCDEF *findnext(FUNCDEF *root, const char *name) +{ + if (root == NULL) + return NULL; + for (FUNCDEF *entry = root->next; entry != NULL; entry = entry->next) { + FUNCDEF *sub = findnext(entry->callees, name); + assert(sub == NULL || sub->skip); + if (sub != NULL) + return sub; + if (!entry->skip && (name == NULL || strcmp(entry->name, name) == 0)) { + entry->skip = true; + return entry; + } + } + return NULL; +} + +static void print_graph_reverse(FUNCDEF *root) +{ + FUNCDEF *entry; + while ((entry = findnext(root, NULL)) != NULL) { + entry->skip = true; /* don't find it again */ + assert(entry->name != NULL); + printf("%s:\n", entry->name); + while (entry != NULL) { + FUNCDEF *parent = entry->caller; + int level = 1; + while (parent != NULL) { + for (int indent = 0; indent < level; indent++) + printf(" "); + printf("%s", parent->name); + if (parent->count > 1) + printf(" [%dx]", parent->count); + printf("\n"); + level += 1; + parent = parent->caller; + } + entry = findnext(root, entry->name); + } + } +} + +static void delete_graph(FUNCDEF *root) +{ + while (root->next != NULL) { + FUNCDEF *entry = root->next; + root->next = entry->next; /* unlink first */ + free((void*)entry->name); + delete_graph(entry->callees); + free((void*)entry); + } +} + +static void usage(int status) +{ + printf("\ncalltree - generate a calltree from the output of the function profiling trace\n" + " data (in the Common Trace Format).\n\n" + "Usage: calltree [options] inputfile\n\n" + " The input file must be in CSV format, as saved by the bmtrace utility.\n\n" + "Options:\n" + "-c value The channel number that contains the function entry/exit\n" + " traces. The default channel is 31.\n" + "-r, --reverse Create a reverse tree.\n" + "--enter=name The name for the \"__cyg_profile_func_enter\" function in the\n" + " TSDL file. The default name is \"enter\".\n" + "--exit=name The name for the \"__cyg_profile_func_exit\" function in the TSDL\n" + " file. The default name is \"exit\".\n" + "-v Show version information.\n"); + exit(status); +} + +static void version(int status) +{ + printf("calltree version 1.1.%d.\n", SVNREV_NUM); + printf("Copyright 2022 CompuPhase\nLicensed under the Apache License version 2.0\n"); + exit(status); +} + +static void unknown_option(const char *option) +{ + fprintf(stderr, "Unknown option \"%s\"; use option -h for help.\n", option); + exit(EXIT_FAILURE); +} + +int main(int argc, char *argv[]) +{ + if (argc <= 1) + usage(EXIT_SUCCESS); + + int channel = 31; + bool reverse = false; + char func_enter[64] = "enter"; + char func_exit[64] = "exit"; + char infile[_MAX_PATH] = ""; + /* command line options */ + for (int idx = 1; idx < argc; idx++) { + if (IS_OPTION(argv[idx])) { + switch (argv[idx][1]) { + case '?': + case 'h': + usage(EXIT_SUCCESS); + break; + case 'c': + if (isdigit(argv[idx][2])) { + channel = (int)strtol(argv[idx] + 2, NULL, 10); + } else if (argv[idx][2] == '=' && isdigit(argv[idx][3])) { + channel = (int)strtol(argv[idx] + 3, NULL, 10); + } else if (isdigit(argv[idx + 1][0])) { + idx += 1; + channel = (int)strtol(argv[idx], NULL, 10); + } else { + unknown_option(argv[idx]); + } + break; + case 'r': + reverse = true; + break; + case 'v': + version(EXIT_SUCCESS); + break; + case '-': /* long options, starting with double hyphen */ + if (strcmp(argv[idx] + 2, "reverse") == 0) + reverse = true; + else if (strncmp(argv[idx] + 2, "enter=", 6) == 0 && strlen(argv[idx] + 8) > 0) + strlcpy(func_enter, argv[idx] + 8, sizearray(func_enter)); + else if (strncmp(argv[idx] + 2, "exit=", 5) == 0 && strlen(argv[idx] + 7) > 0) + strlcpy(func_exit, argv[idx] + 7, sizearray(func_exit)); + else + unknown_option(argv[idx]); + break; + default: + unknown_option(argv[idx]); + } + } else { + if (strlen(infile) == 0) + strlcpy(infile, argv[idx], sizearray(infile)); + else + unknown_option(argv[idx]); + } + } + if (strlen(infile) == 0) { + fprintf(stderr, "No input file specified.\n"); + return EXIT_FAILURE; + } + + FILE *fp = fopen(infile, "rt"); + if (fp == NULL) { + fprintf(stderr, "File not found: %s\n", infile); + return EXIT_FAILURE; + } + char line[512]; + while (fgets(line, sizearray(line), fp) != NULL) { + char name[256]; + int type = match_function(line, channel, name, sizearray(name), func_enter, func_exit); + switch (type) { + case TYPE_ENTER: + enter_function(name); + break; + case TYPE_EXIT: + exit_function(name); + break; + } + } + fclose(fp); + + if (reverse) + print_graph_reverse(&calltree); + else + print_graph(&calltree, 0); + delete_graph(&calltree); + return EXIT_SUCCESS; +} + diff --git a/source/decodectf.c b/source/decodectf.c index 7ad73bf..06e5edc 100644 --- a/source/decodectf.c +++ b/source/decodectf.c @@ -280,7 +280,7 @@ static int lookup_symbol(uint32_t address, char *symname, size_t maxlength) { if (symboltable == NULL) return 0; - const DWARF_SYMBOLLIST *sym = dwarf_sym_from_address(symboltable, address, 1); + const DWARF_SYMBOLLIST *sym = dwarf_sym_from_address(symboltable, address & ~1, 1); if (sym == NULL) return 0; assert(sym->name != NULL); @@ -381,7 +381,7 @@ static void format_field(const char *fieldname, const CTF_TYPE *type, const unsi case CLASS_INTEGER: { char txt[128]; uint8_t base = type->base; - if (base < 2 || base > 16) + if ((base < 2 || base > 16) && base != CTF_BASE_ADDR) base = 10; if (type->size > 32) { uint64_t v = 0; @@ -746,7 +746,8 @@ int ctf_decode(const unsigned char *stream, size_t size, long channel) msgbuffer_append(", ", 2); /* next field */ format_field(field->name, &field->type, cache); cache_reset(); - /* move to the next field */ + /* move to the next field (stay in the current state unless this was the + last parameter) */ field = field->next; if (field == NULL) { msgbuffer_append("", 1); /* force zero-terminate msgbuffer */ diff --git a/source/elf-postlink.c b/source/elf-postlink.c index d74bd4b..ec674a4 100644 --- a/source/elf-postlink.c +++ b/source/elf-postlink.c @@ -24,6 +24,7 @@ #include #include #include "elf.h" +#include "svnrev.h" #define FLAG_HEADER 0x01 @@ -37,7 +38,8 @@ static void usage(int flags) "Usage: elf-postlink [mcu] [elf-file]\n\n"); if (flags & FLAG_MCU_LIST) printf("MCU types:\n" - "\tlpc8xx - NXP LPC800, LPC810, LPC820, LPC830 and LPC840 Cortex-M0/M0+ series\n" + "\tlpc8xx - NXP LPC800, LPC810, LPC820, LPC830 and LPC840 Cortex-M0/M0+\n" + "\t series\n" "\tlpc11xx - NXP LPC1100, LPC11C00 and LPC11U00 Cortex-M0+ series\n" "\tlpc15xx - NXP LPC1500 Cortex-M3 series\n" "\tlpc17xx - NXP LPC1700 Cortex-M3 series\n" @@ -48,15 +50,25 @@ static void usage(int flags) "\tlpc43xx - NXP LPC4300 Cortex-M4/M0 series\n"); } +static void version(void) +{ + printf("elf-postlink version 1.1.%d.\n", SVNREV_NUM); + printf("Copyright 2019-2022 CompuPhase\nLicensed under the Apache License version 2.0\n"); +} + int main(int argc, char *argv[]) { uint32_t chksum; int result, idx_file, idx_type; FILE *fp; + if (argc == 2 && strcmp(argv[1], "-v") == 0) { + version(); + return EXIT_SUCCESS; + } if (argc != 3) { usage(FLAG_ALLINFO); - return 1; + return EXIT_SUCCESS; } idx_type = 1; /* assume correct order of command-line options */ @@ -95,6 +107,6 @@ int main(int argc, char *argv[]) break; } - return 0; + return EXIT_SUCCESS; } diff --git a/source/guidriver.c b/source/guidriver.c index 564abe6..9339d0f 100644 --- a/source/guidriver.c +++ b/source/guidriver.c @@ -87,7 +87,7 @@ struct nk_context* guidriver_init(const char *caption, int width, int height, in SetRect(&rect, 0, 0, width, height); if (flags & GUIDRV_RESIZEABLE) { - style = WS_OVERLAPPEDWINDOW; + style = WS_OVERLAPPEDWINDOW | WS_SIZEBOX; exstyle = 0; } else { style = WS_POPUPWINDOW | WS_CAPTION; diff --git a/source/makefile.dep b/source/makefile.dep index f248511..e77b596 100644 --- a/source/makefile.dep +++ b/source/makefile.dep @@ -9,11 +9,11 @@ bmdebug.obj : armdisasm.h bmcommon.h bmp-scan.h bmp-script.h demangle.h \ memdump.h minIni.h minGlue.h noc_file_dialog.h nuklear_mousepointer.h \ nuklear_style.h nuklear_splitter.h nuklear_tooltip.h serialmon.h \ specialfolder.h svd-support.h tcpip.h parsetsdl.h decodectf.h \ - swotrace.h + swotrace.h svnrev.h bmflash.obj : guidriver.h nuklear.h nuklear_config.h noc_file_dialog.h \ nuklear_mousepointer.h nuklear_style.h nuklear_tooltip.h bmcommon.h \ bmp-scan.h bmp-script.h bmp-support.h rs232.h cksum.h elf.h gdb-rsp.h \ - ident.h minIni.h minGlue.h picoro.h tcpip.h specialfolder.h + ident.h minIni.h minGlue.h picoro.h tcpip.h specialfolder.h svnrev.h bmp-scan.obj : bmp-scan.h tcpip.h bmp-script.obj : bmp-script.h specialfolder.h bmp-support.obj : bmp-scan.h bmp-script.h bmp-support.h rs232.h crc32.h \ @@ -22,14 +22,15 @@ bmprofile.obj : bmcommon.h bmp-script.h bmp-scan.h bmp-support.h rs232.h \ dwarf.h elf.h gdb-rsp.h guidriver.h nuklear.h nuklear_config.h \ mcu-info.h minIni.h minGlue.h noc_file_dialog.h \ nuklear_mousepointer.h nuklear_splitter.h nuklear_style.h \ - nuklear_tooltip.h swotrace.h tcpip.h -bmscan.obj : bmp-scan.h tcpip.h + nuklear_tooltip.h swotrace.h tcpip.h svnrev.h +bmscan.obj : bmp-scan.h tcpip.h svnrev.h bmtrace.obj : guidriver.h nuklear.h nuklear_config.h bmcommon.h \ bmp-script.h bmp-scan.h bmp-support.h rs232.h demangle.h dwarf.h \ elf.h gdb-rsp.h mcu-info.h minIni.h minGlue.h noc_file_dialog.h \ nuklear_mousepointer.h nuklear_splitter.h nuklear_style.h \ nuklear_tooltip.h specialfolder.h tcpip.h parsetsdl.h decodectf.h \ - swotrace.h + swotrace.h svnrev.h +calltree.obj : svnrev.h cksum.obj : cksum.h crc32.obj : crc32.h decodectf.obj : demangle.h parsetsdl.h decodectf.h dwarf.h @@ -37,7 +38,7 @@ demangle.obj : demangle.h dirent.obj : dirent.h dwarf.obj : demangle.h elf.h dwarf.h elf.obj : elf.h -elf-postlink.obj : elf.h +elf-postlink.obj : elf.h svnrev.h gdb-rsp.obj : bmp-support.h rs232.h gdb-rsp.h tcpip.h guidriver.obj : guidriver.h nuklear.h nuklear_config.h \ nuklear_mousepointer.h nuklear_gdip.h @@ -63,7 +64,7 @@ svd-support.obj : svd-support.h xmltractor.h swotrace.obj : usb-support.h bmp-scan.h guidriver.h nuklear.h \ nuklear_config.h parsetsdl.h decodectf.h dwarf.h swotrace.h tcpip.obj : bmp-scan.h tcpip.h -tracegen.obj : parsetsdl.h +tracegen.obj : parsetsdl.h svnrev.h usb-support.obj : usb-support.h xmltractor.obj : xmltractor.h @@ -74,13 +75,13 @@ bmdebug.o : armdisasm.h bmcommon.h bmp-scan.h bmp-script.h demangle.h \ memdump.h minIni.h minGlue.h noc_file_dialog.h nuklear_mousepointer.h \ nuklear_style.h nuklear_splitter.h nuklear_tooltip.h serialmon.h \ specialfolder.h svd-support.h tcpip.h parsetsdl.h decodectf.h \ - swotrace.h res/icon_debug_64.h + swotrace.h svnrev.h res/icon_debug_64.h bmflash.o : guidriver.h nuklear.h nuklear_config.h noc_file_dialog.h \ nuklear_mousepointer.h nuklear_style.h nuklear_tooltip.h bmcommon.h \ bmp-scan.h bmp-script.h bmp-support.h rs232.h cksum.h elf.h gdb-rsp.h \ - ident.h minIni.h minGlue.h picoro.h tcpip.h specialfolder.h \ + ident.h minIni.h minGlue.h picoro.h tcpip.h specialfolder.h svnrev.h \ res/icon_download_64.h -bmp-scan.o : bmp-scan.h tcpip.h +bmp-scan.o : bmp-scan.h tcpip.h svnrev.h bmp-script.o : bmp-script.h specialfolder.h bmp-support.o : bmp-scan.h bmp-script.h bmp-support.h rs232.h crc32.h \ elf.h gdb-rsp.h picoro.h tcpip.h xmltractor.h @@ -88,21 +89,22 @@ bmprofile.o : bmcommon.h bmp-script.h bmp-scan.h bmp-support.h rs232.h \ dwarf.h elf.h gdb-rsp.h guidriver.h nuklear.h nuklear_config.h \ mcu-info.h minIni.h minGlue.h noc_file_dialog.h \ nuklear_mousepointer.h nuklear_splitter.h nuklear_style.h \ - nuklear_tooltip.h swotrace.h tcpip.h res/icon_profile_64.h -bmscan.o : bmp-scan.h tcpip.h + nuklear_tooltip.h swotrace.h tcpip.h svnrev.h res/icon_profile_64.h +bmscan.o : bmp-scan.h tcpip.h svnrev.h bmtrace.o : guidriver.h nuklear.h nuklear_config.h bmcommon.h \ bmp-script.h bmp-scan.h bmp-support.h rs232.h demangle.h dwarf.h \ elf.h gdb-rsp.h mcu-info.h minIni.h minGlue.h noc_file_dialog.h \ nuklear_mousepointer.h nuklear_splitter.h nuklear_style.h \ nuklear_tooltip.h specialfolder.h tcpip.h parsetsdl.h decodectf.h \ - swotrace.h res/icon_trace_64.h + swotrace.h svnrev.h res/icon_trace_64.h +calltree.o : svnrev.h cksum.o : cksum.h crc32.o : crc32.h decodectf.o : demangle.h parsetsdl.h decodectf.h dwarf.h demangle.o : demangle.h dwarf.o : demangle.h dwarf.h elf.h elf.o : elf.h -elf-postlink.o : elf.h +elf-postlink.o : elf.h svnrev.h gdb-rsp.o : bmp-support.h rs232.h gdb-rsp.h tcpip.h guidriver.o : guidriver.h nuklear.h nuklear_config.h \ nuklear_mousepointer.h findfont.h lodepng.h \ @@ -131,7 +133,7 @@ svd-support.o : svd-support.h xmltractor.h swotrace.o : usb-support.h bmp-scan.h guidriver.h nuklear.h \ nuklear_config.h parsetsdl.h decodectf.h dwarf.h swotrace.h tcpip.o : bmp-scan.h tcpip.h -tracegen.o : parsetsdl.h +tracegen.o : parsetsdl.h svnrev.h usb-support.o : usb-support.h xmltractor.o : xmltractor.h diff --git a/source/memdump.c b/source/memdump.c index a38f869..4bfd040 100644 --- a/source/memdump.c +++ b/source/memdump.c @@ -23,6 +23,7 @@ #include #include "guidriver.h" #include "memdump.h" +#include "nuklear_style.h" #if !defined sizearray #define sizearray(e) (sizeof(e) / sizeof((e)[0])) @@ -309,7 +310,7 @@ void memdump_widget(struct nk_context *ctx, MEMDUMP *memdump, int widgetheight, font = ctx->style.font; nk_layout_row_dynamic(ctx, widgetheight, 1); - nk_style_push_color(ctx, &ctx->style.window.fixed_background.data.color, nk_rgba(20, 29, 38, 225)); + nk_style_push_color(ctx, &ctx->style.window.fixed_background.data.color, COLOUR_BG0); if (nk_group_begin(ctx, "memory", 0)) { struct nk_rect rcwidget = nk_layout_widget_bounds(ctx); int col = 0; @@ -363,7 +364,7 @@ void memdump_widget(struct nk_context *ctx, MEMDUMP *memdump, int widgetheight, field[len] = '\0'; nk_layout_row_push(ctx, memdump->item_width); if (modified) - nk_label_colored(ctx, field, NK_TEXT_LEFT, nk_rgb(255, 128, 150)); + nk_label_colored(ctx, field, NK_TEXT_LEFT, COLOUR_FG_RED); else nk_label(ctx, field, NK_TEXT_LEFT); /* advance to next field */ diff --git a/source/noc_file_dialog.c b/source/noc_file_dialog.c index f8f3d7d..0412aff 100644 --- a/source/noc_file_dialog.c +++ b/source/noc_file_dialog.c @@ -21,22 +21,22 @@ * IN THE SOFTWARE. */ +#include #include #include #include "noc_file_dialog.h" -static char *g_noc_file_dialog_ret = NULL; - #ifdef NOC_FILE_DIALOG_GTK #include -const char *noc_file_dialog_open(int flags, - const char *filters, - const char *default_path, - const char *default_name, - const char *caption, - const void *parent) +int noc_file_dialog_open(char *path, size_t pathsize, /* output */ + int flags, + const char *filters, + const char *default_path, + const char *default_name, + const char *caption, + const void *parent) { GtkWidget *dialog; GtkFileFilter *filter; @@ -80,14 +80,21 @@ const char *noc_file_dialog_open(int flags, res = gtk_dialog_run(GTK_DIALOG(dialog)); - free(g_noc_file_dialog_ret); - g_noc_file_dialog_ret = NULL; - - if (res == GTK_RESPONSE_ACCEPT) - g_noc_file_dialog_ret = gtk_file_chooser_get_filename(chooser); + assert(path != NULL && pathsize > 0); + if (res == GTK_RESPONSE_ACCEPT) { + gchar *ptr = gtk_file_chooser_get_filename(chooser); + if (ptr != NULL) { + strncpy(path, ptr, pathsize); + path[pathsize - 1] = '\0'; + g_free(ptr); + } else { + res = GTK_RESPONSE_CANCEL; + } + } gtk_widget_destroy(dialog); - while (gtk_events_pending()) gtk_main_iteration(); - return g_noc_file_dialog_ret; + while (gtk_events_pending()) + gtk_main_iteration(); + return (res == GTK_RESPONSE_ACCEPT); } #endif @@ -101,12 +108,13 @@ const char *noc_file_dialog_open(int flags, #define strdup(s) _strdup(s) #endif -const char *noc_file_dialog_open(int flags, - const char *filters, - const char *default_path, - const char *default_name, - const char *caption, - const void *parent) +int noc_file_dialog_open(char *path, size_t pathsize, /* output */ + int flags, + const char *filters, + const char *default_path, + const char *default_name, + const char *caption, + const void *parent) { OPENFILENAME ofn; // common dialog box structure char szFile[260] = ""; // buffer for file name @@ -134,9 +142,13 @@ const char *noc_file_dialog_open(int flags, else ret = GetOpenFileName(&ofn); - free(g_noc_file_dialog_ret); - g_noc_file_dialog_ret = ret ? strdup(szFile) : NULL; - return g_noc_file_dialog_ret; + assert(path != NULL && pathsize > 0); + if (ret) { + strncpy(path, szFile, pathsize); + path[pathsize - 1] = '\0'; + } + + return ret; } #endif @@ -145,12 +157,13 @@ const char *noc_file_dialog_open(int flags, #include -const char *noc_file_dialog_open(int flags, - const char *filters, - const char *default_path, - const char *default_name, - const char *caption, - const void *parent) +int noc_file_dialog_open(char *path, size_t pathsize, /* output */ + int flags, + const char *filters, + const char *default_path, + const char *default_name, + const char *caption, + const void *parent) { NSURL *url; const char *utf8_path; @@ -158,6 +171,7 @@ const char *noc_file_dialog_open(int flags, NSOpenPanel *open_panel; NSMutableArray *types_array; NSURL *default_url; + int result = 0; // XXX: I don't know about memory management with cococa, need to check // if I leak memory here. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; @@ -192,16 +206,18 @@ const char *noc_file_dialog_open(int flags, [panel setAllowedFileTypes:types_array]; } - free(g_noc_file_dialog_ret); - g_noc_file_dialog_ret = NULL; + assert(path != NULL && pathsize > 0); + path[0] = '\0'; if ( [panel runModal] == NSModalResponseOK ) { url = [panel URL]; utf8_path = [[url path] UTF8String]; - g_noc_file_dialog_ret = strdup(utf8_path); + strncpy(path, utf8_path, pathsize); + path[pathsize - 1] = '\0'; + result = 1; } [pool release]; - return g_noc_file_dialog_ret; + return result; } #endif diff --git a/source/noc_file_dialog.h b/source/noc_file_dialog.h index 6ac2a19..6db52bf 100644 --- a/source/noc_file_dialog.h +++ b/source/noc_file_dialog.h @@ -62,10 +62,11 @@ enum { * managed by the library. The string is valid until the next call to * no_dialog_open. If the user canceled, the return value is NULL. */ -const char *noc_file_dialog_open(int flags, - const char *filters, - const char *default_path, - const char *default_name, - const char *caption, - const void *parent); +int noc_file_dialog_open(char *path, size_t pathsize, /* output */ + int flags, + const char *filters, + const char *default_path, + const char *default_name, + const char *caption, + const void *parent); diff --git a/source/nuklear.c b/source/nuklear.c index 5c72e72..42004d2 100644 --- a/source/nuklear.c +++ b/source/nuklear.c @@ -3738,7 +3738,7 @@ nk_draw_list_add_clip(struct nk_draw_list *list, struct nk_rect rect) NK_ASSERT(list); if (!list) return; if (!list->cmd_count) { - nk_draw_list_push_command(list, rect, list->config.null.texture); + nk_draw_list_push_command(list, rect, list->config.tex_null.texture); } else { struct nk_draw_command *prev = nk_draw_list_command_last(list); if (prev->elem_count == 0) @@ -4093,7 +4093,7 @@ nk_draw_list_stroke_poly_line(struct nk_draw_list *list, const struct nk_vec2 *p /* fill vertices */ for (i = 0; i < points_count; ++i) { - const struct nk_vec2 uv = list->config.null.uv; + const struct nk_vec2 uv = list->config.tex_null.uv; vtx = nk_draw_vertex(vtx, &list->config, points[i], uv, col); vtx = nk_draw_vertex(vtx, &list->config, temp[i*2+0], uv, col_trans); vtx = nk_draw_vertex(vtx, &list->config, temp[i*2+1], uv, col_trans); @@ -4158,7 +4158,7 @@ nk_draw_list_stroke_poly_line(struct nk_draw_list *list, const struct nk_vec2 *p /* add vertices */ for (i = 0; i < points_count; ++i) { - const struct nk_vec2 uv = list->config.null.uv; + const struct nk_vec2 uv = list->config.tex_null.uv; vtx = nk_draw_vertex(vtx, &list->config, temp[i*4+0], uv, col_trans); vtx = nk_draw_vertex(vtx, &list->config, temp[i*4+1], uv, col); vtx = nk_draw_vertex(vtx, &list->config, temp[i*4+2], uv, col); @@ -4179,7 +4179,7 @@ nk_draw_list_stroke_poly_line(struct nk_draw_list *list, const struct nk_vec2 *p for (i1 = 0; i1 < count; ++i1) { float dx, dy; - const struct nk_vec2 uv = list->config.null.uv; + const struct nk_vec2 uv = list->config.tex_null.uv; const nk_size i2 = ((i1+1) == points_count) ? 0 : i1 + 1; const struct nk_vec2 p1 = points[i1]; const struct nk_vec2 p2 = points[i2]; @@ -4289,7 +4289,7 @@ nk_draw_list_fill_poly_convex(struct nk_draw_list *list, /* add vertices + indexes */ for (i0 = points_count-1, i1 = 0; i1 < points_count; i0 = i1++) { - const struct nk_vec2 uv = list->config.null.uv; + const struct nk_vec2 uv = list->config.tex_null.uv; struct nk_vec2 n0 = normals[i0]; struct nk_vec2 n1 = normals[i1]; struct nk_vec2 dm = nk_vec2_muls(nk_vec2_add(n0, n1), 0.5f); @@ -4326,7 +4326,7 @@ nk_draw_list_fill_poly_convex(struct nk_draw_list *list, if (!vtx || !ids) return; for (i = 0; i < vtx_count; ++i) - vtx = nk_draw_vertex(vtx, &list->config, points[i], list->config.null.uv, col); + vtx = nk_draw_vertex(vtx, &list->config, points[i], list->config.tex_null.uv, col); for (i = 2; i < points_count; ++i) { ids[0] = (nk_draw_index)index; ids[1] = (nk_draw_index)(index+ i - 1); @@ -4355,8 +4355,8 @@ nk_draw_list_path_line_to(struct nk_draw_list *list, struct nk_vec2 pos) nk_draw_list_add_clip(list, nk_null_rect); cmd = nk_draw_list_command_last(list); - if (cmd && cmd->texture.ptr != list->config.null.texture.ptr) - nk_draw_list_push_image(list, list->config.null.texture); + if (cmd && cmd->texture.ptr != list->config.tex_null.texture.ptr) + nk_draw_list_push_image(list, list->config.tex_null.texture); points = nk_draw_list_alloc_path(list, 1); if (!points) return; @@ -4558,7 +4558,7 @@ nk_draw_list_fill_rect_multi_color(struct nk_draw_list *list, struct nk_rect rec NK_ASSERT(list); if (!list) return; - nk_draw_list_push_image(list, list->config.null.texture); + nk_draw_list_push_image(list, list->config.tex_null.texture); index = (nk_draw_index)list->vertex_count; vtx = nk_draw_list_alloc_vertices(list, 4); idx = nk_draw_list_alloc_elements(list, 6); @@ -4568,10 +4568,10 @@ nk_draw_list_fill_rect_multi_color(struct nk_draw_list *list, struct nk_rect rec idx[2] = (nk_draw_index)(index+2); idx[3] = (nk_draw_index)(index+0); idx[4] = (nk_draw_index)(index+2); idx[5] = (nk_draw_index)(index+3); - vtx = nk_draw_vertex(vtx, &list->config, nk_vec2(rect.x, rect.y), list->config.null.uv, col_left); - vtx = nk_draw_vertex(vtx, &list->config, nk_vec2(rect.x + rect.w, rect.y), list->config.null.uv, col_top); - vtx = nk_draw_vertex(vtx, &list->config, nk_vec2(rect.x + rect.w, rect.y + rect.h), list->config.null.uv, col_right); - vtx = nk_draw_vertex(vtx, &list->config, nk_vec2(rect.x, rect.y + rect.h), list->config.null.uv, col_bottom); + vtx = nk_draw_vertex(vtx, &list->config, nk_vec2(rect.x, rect.y), list->config.tex_null.uv, col_left); + vtx = nk_draw_vertex(vtx, &list->config, nk_vec2(rect.x + rect.w, rect.y), list->config.tex_null.uv, col_top); + vtx = nk_draw_vertex(vtx, &list->config, nk_vec2(rect.x + rect.w, rect.y + rect.h), list->config.tex_null.uv, col_right); + vtx = nk_draw_vertex(vtx, &list->config, nk_vec2(rect.x, rect.y + rect.h), list->config.tex_null.uv, col_bottom); } NK_API void nk_draw_list_fill_triangle(struct nk_draw_list *list, struct nk_vec2 a, @@ -12059,6 +12059,17 @@ nk_input_has_mouse_click(const struct nk_input *i, enum nk_buttons id) NK_API nk_bool nk_input_has_mouse_click_in_rect(const struct nk_input *i, enum nk_buttons id, struct nk_rect b) +{ + const struct nk_mouse_button *btn; + if (!i) return nk_false; + btn = &i->mouse.buttons[id]; + if (!NK_INBOX(btn->clicked_pos.x,btn->clicked_pos.y,b.x,b.y,b.w,b.h)) + return nk_false; + return nk_true; +} +NK_API nk_bool +nk_input_has_mouse_click_in_button_rect(const struct nk_input *i, enum nk_buttons id, + struct nk_rect b) { const struct nk_mouse_button *btn; if (!i) return nk_false; @@ -18034,7 +18045,7 @@ nk_button_behavior(nk_flags *state, struct nk_rect r, *state = NK_WIDGET_STATE_HOVERED; if (nk_input_is_mouse_down(i, NK_BUTTON_LEFT)) *state = NK_WIDGET_STATE_ACTIVE; - if (nk_input_has_mouse_click_in_rect(i, NK_BUTTON_LEFT, r)) { + if (nk_input_has_mouse_click_in_button_rect(i, NK_BUTTON_LEFT, r)) { ret = (behavior != NK_BUTTON_DEFAULT) ? nk_input_is_mouse_down(i, NK_BUTTON_LEFT): #ifdef NK_BUTTON_TRIGGER_ON_RELEASE @@ -20388,7 +20399,7 @@ nk_textedit_text(struct nk_text_edit *state, const char *text, int total_len) text+text_len, 1)) { nk_textedit_makeundo_insert(state, state->cursor, 1); - ++state->cursor; + state->cursor = NK_MIN(state->cursor + 1, state->string.len); state->has_preferred_x = 0; } } diff --git a/source/nuklear.h b/source/nuklear.h index 3c6f078..de5b42b 100644 --- a/source/nuklear.h +++ b/source/nuklear.h @@ -1188,7 +1188,7 @@ NK_API void nk_input_end(struct nk_context*); /// cfg.curve_segment_count = 22; /// cfg.arc_segment_count = 22; /// cfg.global_alpha = 1.0f; -/// cfg.null = dev->null; +/// cfg.tex_null = dev->tex_null; /// // /// // setup buffers and convert /// struct nk_buffer cmds, verts, idx; @@ -1238,7 +1238,7 @@ struct nk_convert_config { unsigned circle_segment_count; /* number of segments used for circles: default to 22 */ unsigned arc_segment_count; /* number of segments used for arcs: default to 22 */ unsigned curve_segment_count; /* number of segments used for curves: default to 22 */ - struct nk_draw_null_texture null; /* handle to texture with a white pixel for shape drawing */ + struct nk_draw_null_texture tex_null; /* handle to texture with a white pixel for shape drawing */ const struct nk_draw_vertex_layout_element *vertex_layout; /* describes the vertex output format and packing */ nk_size vertex_size; /* sizeof one vertex for vertex packing */ nk_size vertex_alignment; /* vertex alignment: Can be obtained by NK_ALIGNOF */ @@ -4749,6 +4749,7 @@ struct nk_input { NK_API nk_bool nk_input_has_mouse_click(const struct nk_input*, enum nk_buttons); NK_API nk_bool nk_input_has_mouse_click_in_rect(const struct nk_input*, enum nk_buttons, struct nk_rect); +NK_API nk_bool nk_input_has_mouse_click_in_button_rect(const struct nk_input*, enum nk_buttons, struct nk_rect); NK_API nk_bool nk_input_has_mouse_click_down_in_rect(const struct nk_input*, enum nk_buttons, struct nk_rect, nk_bool down); NK_API nk_bool nk_input_is_mouse_click_in_rect(const struct nk_input*, enum nk_buttons, struct nk_rect); NK_API nk_bool nk_input_is_mouse_click_down_in_rect(const struct nk_input *i, enum nk_buttons id, struct nk_rect b, nk_bool down); diff --git a/source/nuklear_glfw_gl2.c b/source/nuklear_glfw_gl2.c index 3dc919e..1c8c271 100644 --- a/source/nuklear_glfw_gl2.c +++ b/source/nuklear_glfw_gl2.c @@ -31,7 +31,7 @@ struct nk_glfw_device { struct nk_buffer cmds; - struct nk_draw_null_texture null; + struct nk_draw_null_texture tex_null; GLuint font_tex; }; @@ -118,7 +118,7 @@ nk_glfw3_render(enum nk_anti_aliasing AA) config.vertex_layout = vertex_layout; config.vertex_size = sizeof(struct nk_glfw_vertex); config.vertex_alignment = NK_ALIGNOF(struct nk_glfw_vertex); - config.null = dev->null; + config.tex_null = dev->tex_null; config.circle_segment_count = 22; config.curve_segment_count = 22; config.arc_segment_count = 22; @@ -264,7 +264,7 @@ nk_glfw3_font_stash_end(void) const void *image; int w, h; image = nk_font_atlas_bake(&glfw.atlas, &w, &h, NK_FONT_ATLAS_RGBA32); nk_glfw3_device_upload_atlas(image, w, h); - nk_font_atlas_end(&glfw.atlas, nk_handle_id((int)glfw.ogl.font_tex), &glfw.ogl.null); + nk_font_atlas_end(&glfw.atlas, nk_handle_id((int)glfw.ogl.font_tex), &glfw.ogl.tex_null); if (glfw.atlas.default_font) nk_style_set_font(&glfw.ctx, &glfw.atlas.default_font->handle); } diff --git a/source/nuklear_style.c b/source/nuklear_style.c index bd86358..0d15c8c 100644 --- a/source/nuklear_style.c +++ b/source/nuklear_style.c @@ -1,7 +1,7 @@ /* * Common styling & layout functions for the Nuklear GUI. * - * Copyright 2021 CompuPhase + * Copyright 2021-2022 CompuPhase * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,37 +27,38 @@ void nuklear_style(struct nk_context *ctx) assert(ctx != NULL); - table[NK_COLOR_TEXT] = nk_rgba(205, 201, 171, 255); - table[NK_COLOR_TEXT_GRAY]= nk_rgba(128, 128, 128, 255); - table[NK_COLOR_WINDOW] = nk_rgba(35, 52, 71, 255); - table[NK_COLOR_HEADER] = nk_rgba(58, 86, 117, 255); - table[NK_COLOR_BORDER] = nk_rgba(128, 128, 128, 255); - table[NK_COLOR_BUTTON] = nk_rgba(58, 86, 117, 255); - table[NK_COLOR_BUTTON_HOVER] = nk_rgba(127, 23, 45, 255); - table[NK_COLOR_BUTTON_ACTIVE] = nk_rgba(127, 23, 45, 255); - table[NK_COLOR_TOGGLE] = nk_rgba(20, 29, 38, 255); - table[NK_COLOR_TOGGLE_HOVER] = nk_rgba(58, 86, 117, 255); - table[NK_COLOR_TOGGLE_CURSOR] = nk_rgba(179, 175, 132, 255); - table[NK_COLOR_SELECT] = nk_rgba(20, 29, 38, 255); - table[NK_COLOR_SELECT_ACTIVE] = nk_rgba(204, 199, 141, 255); - table[NK_COLOR_SLIDER] = nk_rgba(20, 29, 38, 255); - table[NK_COLOR_SLIDER_CURSOR] = nk_rgba(179, 175, 132, 255); - table[NK_COLOR_SLIDER_CURSOR_HOVER] = nk_rgba(127, 23, 45, 255); - table[NK_COLOR_SLIDER_CURSOR_ACTIVE] = nk_rgba(127, 23, 45, 255); - table[NK_COLOR_PROPERTY] = nk_rgba(20, 29, 38, 255); - table[NK_COLOR_EDIT] = nk_rgba(20, 29, 38, 225); - table[NK_COLOR_EDIT_CURSOR] = nk_rgba(205, 201, 171, 255); - table[NK_COLOR_COMBO] = nk_rgba(20, 29, 38, 255); - table[NK_COLOR_CHART] = nk_rgba(20, 29, 38, 255); - table[NK_COLOR_CHART_COLOR] = nk_rgba(170, 40, 60, 255); - table[NK_COLOR_CHART_COLOR_HIGHLIGHT] = nk_rgba(255, 0, 0, 255); - table[NK_COLOR_SCROLLBAR] = nk_rgba(30, 40, 60, 255); - table[NK_COLOR_SCROLLBAR_CURSOR] = nk_rgba(179, 175, 132, 255); - table[NK_COLOR_SCROLLBAR_CURSOR_HOVER] = nk_rgba(204, 199, 141, 255); - table[NK_COLOR_SCROLLBAR_CURSOR_ACTIVE] = nk_rgba(204, 199, 141, 255); - table[NK_COLOR_TAB_HEADER] = nk_rgba(58, 86, 117, 255); - table[NK_COLOR_TOOLTIP] = nk_rgba(204, 199, 141, 255); - table[NK_COLOR_TOOLTIP_TEXT] = nk_rgba(35, 52, 71, 255); + /* adapted from gruvbox palette */ + table[NK_COLOR_TEXT] = nk_rgb_hex("#ebdbb2"); /* fg */ + table[NK_COLOR_TEXT_GRAY]= nk_rgb_hex("#928374"); /* gray-f */ + table[NK_COLOR_WINDOW] = nk_rgb_hex("#32302f"); /* bg0_s */ + table[NK_COLOR_HEADER] = nk_rgb_hex("#076678"); /* blue-b */ + table[NK_COLOR_BORDER] = nk_rgb_hex("#a89984"); /* gray-b */ + table[NK_COLOR_BUTTON] = nk_rgb_hex("#104b5b"); + table[NK_COLOR_BUTTON_HOVER] = nk_rgb_hex("#076678"); /* blue-f in light mode */ + table[NK_COLOR_BUTTON_ACTIVE] = nk_rgb_hex("#076678"); /* blue-f in light mode */ + table[NK_COLOR_TOGGLE] = nk_rgb_hex("#1d2021"); /* bg0_h */ + table[NK_COLOR_TOGGLE_HOVER] = nk_rgb_hex("#928374"); /* gray-f */ + table[NK_COLOR_TOGGLE_CURSOR] = nk_rgb_hex("#458588"); /* blue-b */ + table[NK_COLOR_SELECT] = nk_rgb_hex("#1d2021"); /* bg0_h */ + table[NK_COLOR_SELECT_ACTIVE] = nk_rgb_hex("#fabd2f"); /* yellow-f */ + table[NK_COLOR_SLIDER] = nk_rgb_hex("#1d2021"); /* bg0_h */ + table[NK_COLOR_SLIDER_CURSOR] = nk_rgb_hex("#d79921"); /* yellow-b */ + table[NK_COLOR_SLIDER_CURSOR_HOVER] = nk_rgb_hex("#fabd2f"); /* yellow-f */ + table[NK_COLOR_SLIDER_CURSOR_ACTIVE] = nk_rgb_hex("#fabd2f"); /* yellow-f */ + table[NK_COLOR_PROPERTY] = nk_rgb_hex("#1d2021"); /* bg0_h */ + table[NK_COLOR_EDIT] = nk_rgb_hex("#1d2021"); /* bg0_h */ + table[NK_COLOR_EDIT_CURSOR] = nk_rgb_hex("#fbf1c7"); /* fg0 (bg0 in light mode) */ + table[NK_COLOR_COMBO] = nk_rgb_hex("#1d2021"); /* bg0_h */ + table[NK_COLOR_CHART] = nk_rgb_hex("#1d2021"); /* bg0_h */ + table[NK_COLOR_CHART_COLOR] = nk_rgb_hex("#cc241d"); /* red-b */ + table[NK_COLOR_CHART_COLOR_HIGHLIGHT] = nk_rgb_hex("#fb4934"); /* red-f */ + table[NK_COLOR_SCROLLBAR] = nk_rgb_hex("#1d2021"); /* bg0_h */ + table[NK_COLOR_SCROLLBAR_CURSOR] = nk_rgb_hex("#928374"); /* gray-f */ + table[NK_COLOR_SCROLLBAR_CURSOR_HOVER] = nk_rgb_hex("#a899a4"); /* gray-b */ + table[NK_COLOR_SCROLLBAR_CURSOR_ACTIVE] = nk_rgb_hex("#a899a4");/* gray-b */ + table[NK_COLOR_TAB_HEADER] = nk_rgb_hex("#104b5b"); + table[NK_COLOR_TOOLTIP] = nk_rgb_hex("#fbf1c7"); /* bg0 in light mode, also fg0 */ + table[NK_COLOR_TOOLTIP_TEXT] = nk_rgb_hex("#3c3836"); /* fg in light mode, also bg1 */ nk_style_from_table(ctx, table); diff --git a/source/nuklear_style.h b/source/nuklear_style.h index 98c235e..ebdcd2d 100644 --- a/source/nuklear_style.h +++ b/source/nuklear_style.h @@ -1,7 +1,7 @@ /* * Common styling & layout functions for the Nuklear GUI. * - * Copyright 2021 CompuPhase + * Copyright 2021-2022 CompuPhase * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,4 +24,20 @@ void nuklear_style(struct nk_context *ctx); float *nk_ratio(int count, ...); +#define COLOUR_BG0_S nk_rgb_hex("#32302f") /* window background colour */ +#define COLOUR_BG0 nk_rgb_hex("#1d2021") /* background colour for controls near black) */ +#define COLOUR_BG_RED nk_rgb_hex("#cc241d") +#define COLOUR_BG_YELLOW nk_rgb_hex("#d79921") +#define COLOUR_TEXT nk_rgb_hex("#ebdbb2") +#define COLOUR_HIGHLIGHT nk_rgb_hex("#fff4ca") /* highlighted text */ +#define COLOUR_FG_GRAY nk_rgb_hex("#928374") /* disabled text */ +#define COLOUR_FG_YELLOW nk_rgb_hex("#fabd2f") +#define COLOUR_FG_RED nk_rgb_hex("#fb4934") +#define COLOUR_FG_GREEN nk_rgb_hex("#0ad074") +#define COLOUR_FG_BLUE nk_rgb_hex("#83a598") +#define COLOUR_FG_PURPLE nk_rgb_hex("#d3869b") +#define COLOUR_FG_AQUA nk_rgb_hex("#8ec07c") + +#define SWO_TRACE_DEFAULT_COLOR COLOUR_TEXT + #endif /* _NUKLEAR_STYLE_H */ diff --git a/source/parsetsdl.c b/source/parsetsdl.c index d3696e2..669a4fe 100644 --- a/source/parsetsdl.c +++ b/source/parsetsdl.c @@ -80,6 +80,7 @@ enum { TOK_UNSIGNED, TOK_VARIANT, TOK_VOID, + TOK_INCLUDE, /* multi-character operators */ TOK_OP_TYPE_ASSIGN, TOK_OP_ARROW, @@ -102,8 +103,14 @@ typedef struct tagTOKEN { int pushed; } TOKEN; +typedef struct tagINCLUDEFILE { + FILE *file; + int linenr; +} INCLUDEFILE; +#define INCLUDE_NESTING 8 static FILE *inputfile = NULL; +static INCLUDEFILE includestack[INCLUDE_NESTING] = { NULL }; static char *linebuffer = NULL; static int linebuffer_index = 0; static int linenumber = 0; @@ -137,7 +144,7 @@ static int recent_error = -1; switch (code) { case CTFERR_FILEOPEN: - vsprintf(message, "File open error (file not found?)", args); + vsprintf(message, "File open error on %s (file not found?)", args); break; case CTFERR_MEMORY: vsprintf(message, "Memory allocation error", args); @@ -203,6 +210,9 @@ static int recent_error = -1; case CTFERR_CLOCK_IS_INT: vsprintf(message, "Clock must be mapped to integer type", args); break; + case CTFERR_EXCEED_INCLUDES: + vsprintf(message, "#includes too deeply nested", args); + break; default: assert(code != CTFERR_NONE); sprintf(message, "Unknown error, code %d", code); @@ -218,10 +228,14 @@ static int readline_init(const char *filename) { linenumber = 0; comment_block_start = 0; + for (int idx = 0; idx < INCLUDE_NESTING; idx++) { + includestack[idx].file = NULL; + includestack[idx].linenr = 0; + } inputfile = fopen(filename, "rt"); if (inputfile == NULL) - return ctf_error(CTFERR_FILEOPEN); + return ctf_error(CTFERR_FILEOPEN, filename); linebuffer = (char*)malloc(MAX_LINE_LENGTH * sizeof(char)); if (linebuffer == NULL) { @@ -234,23 +248,42 @@ static int readline_init(const char *filename) static void readline_cleanup(void) { - if (inputfile != NULL) + if (inputfile != NULL) { fclose(inputfile); - if (linebuffer != NULL) + inputfile = NULL; + } + if (linebuffer != NULL) { free((void*)linebuffer); + linebuffer = NULL; + } + for (int idx = 0; idx < INCLUDE_NESTING; idx++) + if (includestack[idx].file != NULL) { + fclose(includestack[idx].file); + includestack[idx].file = NULL; + } } static int readline_next(void) { assert(inputfile != NULL); assert(linebuffer != NULL); - for (;; ) { + for ( ;; ) { char *ptr; char in_quotes; - if (fgets(linebuffer, MAX_LINE_LENGTH - 1, inputfile)== NULL) { + if (fgets(linebuffer, MAX_LINE_LENGTH - 1, inputfile) == NULL) { if (comment_block_start > 0) ctf_error(CTFERR_BLOCKCOMMENT, comment_block_start); - return 0; /* no more data in the file */ + /* no more data in the file, try to pop a file from the include stack */ + int top; + for (top = INCLUDE_NESTING - 1; top >= 0 && includestack[top].file == NULL; top--) + {} + if (top < 0) + return 0; /* no more files -> done */ + fclose(inputfile); + inputfile = includestack[top].file; + linenumber = includestack[top].linenr; + includestack[top].file = NULL; + continue; } linenumber += 1; @@ -461,7 +494,8 @@ static const char *token_keywords[] = { "align", "callsite", "char", "const", "clock", "double", "enum", "env", "event", "fields", "float", "floating_point", "header", "int", "integer", "long", "packet", "short", "signed", "stream", "string", "struct", "trace", - "typealias", "typedef", "unsigned", "variant", "void" }; + "typealias", "typedef", "unsigned", "variant", "void", + "#include" }; static const char *token_operators[] = { ":=", "->", "::", "..." }; static const char *token_generic[] = { @@ -950,15 +984,15 @@ static void parse_typealias_fields(CTF_TYPE *type) const char *p; token_need(TOK_IDENTIFIER); p = token_gettext(); - if (strcmp(p, "decimal") || strcmp(p, "dec") || strcmp(p, "d") || strcmp(p, "i")) { + if (strcmp(p, "decimal") == 0 || strcmp(p, "dec") == 0 || strcmp(p, "d") == 0 || strcmp(p, "i") == 0) { type->base = 10; - } else if (strcmp(p, "hexadecimal") || strcmp(p, "hex") || stricmp(p, "x")) { + } else if (strcmp(p, "hexadecimal") == 0 || strcmp(p, "hex") == 0 || stricmp(p, "x") == 0) { type->base = 16; - } else if (strcmp(p, "octal") || strcmp(p, "oct") || stricmp(p, "o")) { + } else if (strcmp(p, "octal") == 0 || strcmp(p, "oct") == 0 || stricmp(p, "o") == 0) { type->base = 8; - } else if (strcmp(p, "binary") || stricmp(p, "b")) { + } else if (strcmp(p, "binary") == 0 || stricmp(p, "b") == 0) { type->base = 2; - } else if (strcmp(p, "symaddress") || stricmp(p, "symaddr")) { + } else if (strcmp(p, "symaddress") == 0 || strcmp(p, "symaddr") == 0) { type->base = CTF_BASE_ADDR; type->flags &= ~TYPEFLAG_SIGNED; } @@ -1290,7 +1324,7 @@ static void parse_event_header(CTF_EVENT_HEADER *evthdr, CTF_TYPE **clock) CTF_TYPE *field; assert(knowntype->fields != NULL); for (field = knowntype->fields->next; field != NULL; field = field->next) { - if (strcmp(field->identifier, "event.id")== 0 || strcmp(field->identifier, "id")== 0) { + if (strcmp(field->identifier, "event.id") == 0 || strcmp(field->identifier, "id") == 0) { if (field->typeclass != CLASS_INTEGER || field->length != 0) ctf_error(CTFERR_WRONGTYPE); evthdr->header.id_size = (uint8_t)field->size; @@ -1554,13 +1588,11 @@ static void parse_trace(void) token_need(TOK_IDENTIFIER); ctf_trace.byte_order = (strcmp(token_gettext(), "be") == 0) ? BYTEORDER_BE : BYTEORDER_LE; } else if (strcmp(identifier, "uuid") == 0) { - int idx; - const char *ptr; token_need(TOK_LSTRING); /* convert string to byte array */ memset(ctf_trace.uuid, 0, sizearray(ctf_trace.uuid)); - ptr = token_gettext(); - for (idx = 0; idx < sizearray(ctf_trace.uuid); idx++) { + const char *ptr = token_gettext(); + for (int idx = 0; idx < sizearray(ctf_trace.uuid); idx++) { if (*ptr == '-') ptr++; if (!isxdigit(ptr[0]) || !isxdigit(ptr[1])) @@ -1879,6 +1911,29 @@ static void parse_event(void) ctf_trace.stream_mask |= (1 << event->stream_id); } +static void do_include(void) +{ + int top; + for (top = INCLUDE_NESTING - 1; top >= 0 && includestack[top].file == NULL; top--) + {} + top += 1; /* undo overrun of the loop */ + if (top >= INCLUDE_NESTING) { + ctf_error(CTFERR_EXCEED_INCLUDES); + } else { + if (token_need(TOK_LSTRING) > 0) { + const char *name = token_gettext(); + FILE *fp = fopen(name, "rt"); + if (fp != NULL) { + includestack[top].file = inputfile; + includestack[top].linenr = 0; + inputfile = fp; + } else { + ctf_error(CTFERR_FILEOPEN, name); + } + } + } +} + /** ctf_parse_init() initializes the TSDL parser and sets up default types. * It retuns 1 on success and 0 on error; the error message has then already * been issued via ctf_error_notify(). @@ -1958,6 +2013,9 @@ int ctf_parse_run(void) case TOK_CALLSITE: //??? error: feature not implemented break; + case TOK_INCLUDE: + do_include(); + break; default: ctf_error(CTFERR_SYNTAX_MAIN); } diff --git a/source/parsetsdl.h b/source/parsetsdl.h index 997d5c6..b3e35b3 100644 --- a/source/parsetsdl.h +++ b/source/parsetsdl.h @@ -36,7 +36,7 @@ enum { enum { CTFERR_NONE, - CTFERR_FILEOPEN, /* file open error (file not found?) */ + CTFERR_FILEOPEN, /* file open error (file ... not found?) */ CTFERR_MEMORY, /* memory allocation error */ CTFERR_LONGLINE, /* line too long */ CTFERR_BLOCKCOMMENT, /* comment not closed */ @@ -59,6 +59,7 @@ enum { CTFERR_DUPLICATE_NAME,/* duplicate name ... */ CTFERR_CLOCK_IS_INT, /* clock must be mapped to integer type */ CTFERR_DUPLICATE_SETTING, + CTFERR_EXCEED_INCLUDES,/* #include nesting too deep */ }; enum { diff --git a/source/svnrev.h b/source/svnrev.h new file mode 100644 index 0000000..ffb39d7 --- /dev/null +++ b/source/svnrev.h @@ -0,0 +1,19 @@ +/* This file was generated by the "svnrev" utility + * (https://www.compuphase.com/svnrev.htm). + * You should not modify it manually, as it may be re-generated. + * + * $Revision: 6776$ + * $Date: 2022-10-12$ + */ + +#ifndef _SVNREV_H_ +#define _SVNREV_H_ + +#define SVNREV_NUM 6776 +#define SVNREV_STR "6776" +#define SVNREV_RCS "$Revision: 6776 $" +#define SVNREV_DATE "2022-10-12" +#define SVNREV_STAMP 20221012L +#define SVNREV_MODIFIED 0 + +#endif /* _SVNREV_H_ */ diff --git a/source/swotrace.c b/source/swotrace.c index 6c02fee..892769f 100644 --- a/source/swotrace.c +++ b/source/swotrace.c @@ -2,7 +2,7 @@ * Shared code for SWO Trace for the bmtrace and bmdebug utilities. It uses * WinUSB or libusbK on Microsoft Windows, and libusb 1.0 on Linux. * - * Copyright 2019-2020 CompuPhase + * Copyright 2019-2022 CompuPhase * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -56,6 +56,7 @@ #include "bmp-scan.h" #include "guidriver.h" +#include "nuklear_style.h" #include "parsetsdl.h" #include "decodectf.h" #include "swotrace.h" @@ -75,13 +76,13 @@ #define CHANNEL_NAMELENGTH 30 typedef struct tagCHANNELINFO { - int enabled; + bool enabled; struct nk_color color; char name[CHANNEL_NAMELENGTH]; } CHANNELINFO; static CHANNELINFO channels[NUM_CHANNELS]; -void channel_set(int index, int enabled, const char *name, struct nk_color color) +void channel_set(int index, bool enabled, const char *name, struct nk_color color) { assert(index >= 0 && index < NUM_CHANNELS); channels[index].enabled = enabled; @@ -92,13 +93,13 @@ void channel_set(int index, int enabled, const char *name, struct nk_color color strlcpy(channels[index].name, name, sizearray(channels[index].name)); } -int channel_getenabled(int index) +bool channel_getenabled(int index) { assert(index >= 0 && index < NUM_CHANNELS); return channels[index].enabled; } -void channel_setenabled(int index, int enabled) +void channel_setenabled(int index, bool enabled) { assert(index >= 0 && index < NUM_CHANNELS); channels[index].enabled = enabled; @@ -146,7 +147,7 @@ void channel_setcolor(int index, struct nk_color color) #define PACKET_SIZE 64 -#define PACKET_NUM 32 +#define PACKET_NUM 128 typedef struct tagPACKET { unsigned char data[PACKET_SIZE]; size_t length; @@ -154,7 +155,7 @@ typedef struct tagPACKET { } PACKET; static PACKET trace_queue[PACKET_NUM]; static int tracequeue_head = 0, tracequeue_tail = 0; - +static int tracequeue_overflow = 0; typedef struct tagTRACESTRING { struct tagTRACESTRING *next; @@ -186,7 +187,7 @@ static unsigned itm_packet_errors = 0; #define ITM_CHANNEL(b) (unsigned)(((b) >> 3) & 0x1f) /* get channel number from ITM packet header */ #define ITM_LENGTH(b) (unsigned)(((b) & 0x07) == 3 ? 4 : (b) & 0x07) -void tracestring_add(unsigned channel, const unsigned char *buffer, size_t length, double timestamp) +static void tracestring_add(unsigned channel, const unsigned char *buffer, size_t length, double timestamp) { assert(channel < NUM_CHANNELS); assert(buffer != NULL); @@ -388,8 +389,12 @@ int tracestring_process(bool enabled) } while (pktlen > 0) { - if (!ITM_VALIDHDR(*pktdata)) { - //??? this may be valid profile data + if (*pktdata == 0x17) { + /* profile packet (PC address) */ + pktdata += 5; + pktlen = (pktlen > 5) ? pktlen - 5 : 0; + continue; + } else if (!ITM_VALIDHDR(*pktdata)) { ctf_decode_reset(); itm_packet_errors += 1; goto skip_packet; /* not a valid ITM packet, ignore it */ @@ -432,6 +437,8 @@ int tracestring_process(bool enabled) tracequeue_head = (tracequeue_head + 1) % PACKET_NUM; } + if (!enabled) + tracequeue_overflow = 0; /* ignore overflow events if not running/decoding */ return count; } @@ -550,11 +557,21 @@ short trace_getdatasize(void) return itm_datasize; } -int trace_getpacketerrors(void) +int trace_getpacketerrors(bool reset) { - return itm_packet_errors; + int result = itm_packet_errors; + if (reset) + itm_packet_errors = 0; + return result; } +int trace_overflowerrors(bool reset) +{ + int result = tracequeue_overflow; + if (reset) + tracequeue_overflow = 0; + return result; +} static void addsample(uint32_t pc, unsigned *sample_map, uint32_t code_base, uint32_t code_top) { @@ -900,13 +917,17 @@ static DWORD __stdcall trace_read(LPVOID arg) if (TraceSocket != INVALID_SOCKET) { for ( ;; ) { int result = recv(TraceSocket, (char*)buffer, sizearray(buffer), 0); - int next = (tracequeue_tail + 1) % PACKET_NUM; - if (result > 0 && next != tracequeue_head) { - memcpy(trace_queue[tracequeue_tail].data, buffer, result); - trace_queue[tracequeue_tail].length = result; - trace_queue[tracequeue_tail].timestamp = get_timestamp(); - tracequeue_tail = next; - PostMessage((HWND)guidriver_apphandle(), WM_USER, 0, 0L); /* just a flag to wake up the GUI */ + if (result > 0) { + int next = (tracequeue_tail + 1) % PACKET_NUM; + if (next != tracequeue_head) { + memcpy(trace_queue[tracequeue_tail].data, buffer, result); + trace_queue[tracequeue_tail].length = result; + trace_queue[tracequeue_tail].timestamp = get_timestamp(); + tracequeue_tail = next; + PostMessage((HWND)guidriver_apphandle(), WM_USER, 0, 0L); /* just a flag to wake up the GUI */ + } else { + tracequeue_overflow += 1; /* notify packet queue overflow */ + } } else if (result < 0) { break; } @@ -916,13 +937,17 @@ static DWORD __stdcall trace_read(LPVOID arg) uint32_t numread = 0; if (_WinUsb_ReadPipe(hUSBiface, usbTraceEP, buffer, sizearray(buffer), &numread, NULL)) { /* add the packet to the queue */ - int next = (tracequeue_tail + 1) % PACKET_NUM; - if (numread > 0 && next != tracequeue_head) { - memcpy(trace_queue[tracequeue_tail].data, buffer, numread); - trace_queue[tracequeue_tail].length = numread; - trace_queue[tracequeue_tail].timestamp = get_timestamp(); - tracequeue_tail = next; - PostMessage((HWND)guidriver_apphandle(), WM_USER, 0, 0L); /* just a flag to wake up the GUI */ + if (numread > 0) { + int next = (tracequeue_tail + 1) % PACKET_NUM; + if (next != tracequeue_head) { + memcpy(trace_queue[tracequeue_tail].data, buffer, numread); + trace_queue[tracequeue_tail].length = numread; + trace_queue[tracequeue_tail].timestamp = get_timestamp(); + tracequeue_tail = next; + PostMessage((HWND)guidriver_apphandle(), WM_USER, 0, 0L); /* just a flag to wake up the GUI */ + } else { + tracequeue_overflow += 1; /* notify packet queue overflow */ + } } } else { Sleep(50); @@ -933,13 +958,17 @@ static DWORD __stdcall trace_read(LPVOID arg) uint32_t numread = 0; if (_UsbK_ReadPipe(hUSBiface, usbTraceEP, buffer, sizearray(buffer), &numread, NULL)) { /* add the packet to the queue */ - int next = (tracequeue_tail + 1) % PACKET_NUM; - if (numread > 0 && next != tracequeue_head) { - memcpy(trace_queue[tracequeue_tail].data, buffer, numread); - trace_queue[tracequeue_tail].length = numread; - trace_queue[tracequeue_tail].timestamp = get_timestamp(); - tracequeue_tail = next; - PostMessage((HWND)guidriver_apphandle(), WM_USER, 0, 0L); /* just a flag to wake up the GUI */ + if (numread > 0) { + int next = (tracequeue_tail + 1) % PACKET_NUM; + if (next != tracequeue_head) { + memcpy(trace_queue[tracequeue_tail].data, buffer, numread); + trace_queue[tracequeue_tail].length = numread; + trace_queue[tracequeue_tail].timestamp = get_timestamp(); + tracequeue_tail = next; + PostMessage((HWND)guidriver_apphandle(), WM_USER, 0, 0L); /* just a flag to wake up the GUI */ + } else { + tracequeue_overflow += 1; /* notify packet queue overflow */ + } } } else { Sleep(50); @@ -958,6 +987,7 @@ int trace_init(unsigned short endpoint, const char *ipaddress) { loc_errno = 0; win_errno = 0; + tracequeue_overflow = 0; if (hThread != NULL && hUSBiface != INVALID_HANDLE_VALUE) return TRACESTAT_OK; /* double initialization */ @@ -999,7 +1029,7 @@ int trace_init(unsigned short endpoint, const char *ipaddress) win_errno = GetLastError(); return TRACESTAT_NO_THREAD; } - SetThreadPriority(hThread, THREAD_PRIORITY_ABOVE_NORMAL); + SetThreadPriority(hThread, THREAD_PRIORITY_HIGHEST); return TRACESTAT_OK; } @@ -1059,7 +1089,7 @@ double get_timestamp(void) { struct timeval tv; gettimeofday(&tv,NULL); - return 1000000.0 * tv.tv_sec + tv.tv_usec; + return tv.tv_sec + tv.tv_usec / 1000000.0; } static void *trace_read(void *arg) @@ -1071,12 +1101,16 @@ static void *trace_read(void *arg) while (!force_exit && hThread != 0 && hUSBiface != NULL) { if (libusb_bulk_transfer(hUSBiface, usbTraceEP, buffer, sizeof(buffer), &numread, 0) == 0) { /* add the packet to the queue */ - int next = (tracequeue_tail + 1) % PACKET_NUM; - if (numread > 0 && next != tracequeue_head) { - memcpy(trace_queue[tracequeue_tail].data, buffer, numread); - trace_queue[tracequeue_tail].length = numread; - trace_queue[tracequeue_tail].timestamp = get_timestamp(); - tracequeue_tail = next; + if (numread > 0) { + int next = (tracequeue_tail + 1) % PACKET_NUM; + if (next != tracequeue_head) { + memcpy(trace_queue[tracequeue_tail].data, buffer, numread); + trace_queue[tracequeue_tail].length = numread; + trace_queue[tracequeue_tail].timestamp = get_timestamp(); + tracequeue_tail = next; + } else { + tracequeue_overflow += 1; /* notify packet queue overflow */ + } } } } @@ -1138,6 +1172,7 @@ int trace_init(unsigned short endpoint, const char *ipaddress) { int result; + tracequeue_overflow = 0; usbTraceEP = endpoint; if (hThread != 0 && hUSBiface != NULL) @@ -1258,7 +1293,7 @@ float tracelog_labelwidth(float rowheight) float labelwidth = 0; for (idx = 0; idx < NUM_CHANNELS; idx++) { int len = strlen(channels[idx].name); - if (channels[idx].enabled && labelwidth < len) + if (labelwidth < len) labelwidth = len; } return labelwidth * (rowheight / 2); @@ -1290,7 +1325,7 @@ void tracelog_widget(struct nk_context *ctx, const char *id, float rowheight, in tstampwidth = (int)((tstampwidth * rowheight) / 2) + 10; /* (near) black background on group */ - nk_style_push_color(ctx, &stwin->fixed_background.data.color, nk_rgba(20, 29, 38, 225)); + nk_style_push_color(ctx, &stwin->fixed_background.data.color, COLOUR_BG0); if (nk_group_begin(ctx, id, widget_flags)) { static int recent_markline = -1; static int scrollpos = 0; @@ -1337,8 +1372,8 @@ void tracelog_widget(struct nk_context *ctx, const char *id, float rowheight, in if (lines == markline) { stbtn.normal.data.color = stbtn.hover.data.color = stbtn.active.data.color = stbtn.text_background - = nk_rgb(0, 0, 0); - stbtn.text_normal = stbtn.text_active = stbtn.text_hover = nk_rgb(255, 255, 128); + = COLOUR_BG0; + stbtn.text_normal = stbtn.text_active = stbtn.text_hover = COLOUR_FG_YELLOW; nk_button_symbol_styled(ctx, &stbtn, NK_SYMBOL_TRIANGLE_RIGHT); } else { nk_spacing(ctx, 1); @@ -1350,21 +1385,21 @@ void tracelog_widget(struct nk_context *ctx, const char *id, float rowheight, in = channels[item->channel].color; struct nk_color clrtxt; if (channels[item->channel].color.r + 2 * channels[item->channel].color.g + channels[item->channel].color.b < 700) - clrtxt = nk_rgb(255,255,255); + clrtxt = COLOUR_HIGHLIGHT; else - clrtxt = nk_rgb(20,29,38); + clrtxt = COLOUR_BG0; stbtn.text_normal = stbtn.text_active = stbtn.text_hover = clrtxt; nk_layout_row_push(ctx, labelwidth); nk_button_label_styled(ctx, &stbtn, channels[item->channel].name); /* timestamp (relative time since previous trace) */ nk_layout_row_push(ctx, tstampwidth); - nk_label_colored(ctx, item->timefmt, NK_TEXT_RIGHT, nk_rgb(255, 255, 128)); + nk_label_colored(ctx, item->timefmt, NK_TEXT_RIGHT, COLOUR_FG_YELLOW); /* calculate size of the text */ assert(font != NULL && font->width != NULL); int textwidth = (int)font->width(font->userdata, font->height, item->text, item->length) + 10; nk_layout_row_push(ctx, textwidth); if (lines == markline) - nk_text_colored(ctx, item->text, item->length, NK_TEXT_LEFT, nk_rgb(255, 255, 128)); + nk_text_colored(ctx, item->text, item->length, NK_TEXT_LEFT, COLOUR_FG_YELLOW); else nk_text(ctx, item->text, item->length, NK_TEXT_LEFT); nk_layout_row_end(ctx); @@ -1374,11 +1409,11 @@ void tracelog_widget(struct nk_context *ctx, const char *id, float rowheight, in for (item = statusmessage_root.next; item != NULL; item = item->next) { struct nk_color clr; if (item->flags < 0) - clr = nk_rgb(255, 80, 100); + clr = COLOUR_FG_RED; else if (item->channel == TRACESTATMSG_CTF) - clr = nk_rgb(128, 224, 128); + clr = COLOUR_FG_AQUA; else - clr = nk_rgb(100, 255, 100); + clr = COLOUR_FG_YELLOW; nk_layout_row_dynamic(ctx, rowheight, 1); nk_label_colored(ctx, item->text, NK_TEXT_LEFT, clr); lines++; @@ -1550,8 +1585,6 @@ double timeline_widget(struct nk_context *ctx, const char *id, float rowheight, /* preset common parts of the new button style */ stbtn = ctx->style.button; - // stbtn.border = 0; - // stbtn.rounding = 0; stbtn.padding.x = stbtn.padding.y = 0; /* check the length of the longest channel name */ @@ -1560,7 +1593,7 @@ double timeline_widget(struct nk_context *ctx, const char *id, float rowheight, /* no spacing & black background on group */ nk_style_push_vec2(ctx, &ctx->style.window.spacing, nk_vec2(0, 0)); - nk_style_push_color(ctx, &ctx->style.window.fixed_background.data.color, nk_rgba(20, 29, 38, 225)); + nk_style_push_color(ctx, &ctx->style.window.fixed_background.data.color, COLOUR_BG0); if (nk_group_begin(ctx, id, widget_flags | NK_WINDOW_NO_SCROLLBAR)) { static float timeline_maxpos_prev = 0.0f; struct nk_window *win = ctx->current; @@ -1594,7 +1627,7 @@ double timeline_widget(struct nk_context *ctx, const char *id, float rowheight, nk_layout_row_begin(ctx, NK_STATIC, rowheight + VERPADDING, 3); nk_layout_row_push(ctx, rcwidget.w - 2 * (1.5f * rowheight)); struct nk_rect rc = nk_layout_widget_bounds(ctx); - nk_fill_rect(&win->buffer, rc, 0.0f, nk_rgb(35, 52, 71)); + nk_fill_rect(&win->buffer, rc, 0.0f, COLOUR_BG0_S); x2 = rc.x + rc.w; int submark_iter = 0; long mark_stamp = 0; @@ -1604,27 +1637,27 @@ double timeline_widget(struct nk_context *ctx, const char *id, float rowheight, struct nk_color clr; if (mark_stamp % mark_inv_scale == 0) { sprintf(valstr, "%ld s", mark_stamp / mark_inv_scale); - clr = nk_rgb(255, 255, 220); + clr = COLOUR_FG_YELLOW; } else { sprintf(valstr, "+%ld %s", mark_stamp, unit); - clr = nk_rgb(144, 144, 128); + clr = COLOUR_TEXT; } nk_stroke_line(&win->buffer, x1, rc.y, x1, rc.y + rowheight - 2, 1, clr); rc.x = x1 + 2; rc.w = x2 - rc.x; - nk_draw_text(&win->buffer, rc, valstr, strlen(valstr), font, nk_rgb(35, 52, 71), clr); + nk_draw_text(&win->buffer, rc, valstr, strlen(valstr), font, COLOUR_BG0, clr); mark_stamp += mark_deltatime; } else { - nk_stroke_line(&win->buffer, x1, rc.y, x1, rc.y + rowheight / 2 - 2, 1, nk_rgb(144, 144, 128)); + nk_stroke_line(&win->buffer, x1, rc.y, x1, rc.y + rowheight / 2 - 2, 1, COLOUR_TEXT); } if (++submark_iter == submark_count) submark_iter = 0; } rc = nk_layout_widget_bounds(ctx); - nk_stroke_line(&win->buffer, rc.x, rc.y + rc.h, rc.x + rc.w - labelwidth - HORPADDING, rc.y + rc.h, 1, nk_rgb(80, 80, 80)); + nk_stroke_line(&win->buffer, rc.x, rc.y + rc.h, rc.x + rc.w - HORPADDING, rc.y + rc.h, 1, COLOUR_FG_GRAY); rc.w = labelwidth; rc.h -= 1; - nk_fill_rect(&win->buffer, rc, 0.0f, nk_rgb(20, 29, 38)); + nk_fill_rect(&win->buffer, rc, 0.0f, COLOUR_BG0); nk_spacing(ctx, 1); nk_layout_row_push(ctx, 1.5f * rowheight); if (nk_button_symbol_styled(ctx, &stbtn, NK_SYMBOL_PLUS)) { @@ -1670,7 +1703,7 @@ double timeline_widget(struct nk_context *ctx, const char *id, float rowheight, float textwidth; int len; if (!channels[chan].enabled) - continue; /* only draw enable channels */ + continue; /* only draw enabled channels */ nk_layout_row_dynamic(ctx, rowheight + VERPADDING, 1); rc = nk_layout_widget_bounds(ctx); rc.x += HORPADDING; @@ -1679,9 +1712,9 @@ double timeline_widget(struct nk_context *ctx, const char *id, float rowheight, rc.h -= 1; nk_fill_rect(&win->buffer, rc, 0.0f, channels[chan].color); if (channels[chan].color.r + 2 * channels[chan].color.g + channels[chan].color.b < 700) - clrtxt = nk_rgb(255,255,255); + clrtxt = COLOUR_HIGHLIGHT; else - clrtxt = nk_rgb(20,29,38); + clrtxt = COLOUR_BG0; /* center the text in the rect */ len = strlen(channels[chan].name); textwidth = font->width(font->userdata, font->height, channels[chan].name, len); @@ -1704,13 +1737,13 @@ double timeline_widget(struct nk_context *ctx, const char *id, float rowheight, rc = nk_layout_widget_bounds(ctx); rc.y -= yscroll; if (row & 1) - nk_fill_rect(&win->buffer, rc, 0.0f, nk_rgb(30, 40, 50)); + nk_fill_rect(&win->buffer, rc, 0.0f, COLOUR_BG0_S); row++; /* draw marks for each active channel */ for (idx = 0; idx < (int)timeline[chan].length; idx++) { float x = timeline[chan].marks[idx].pos + labelwidth + 2 * HORPADDING - xscroll; float y = 0.75f * rowheight * (1 - (float)timeline[chan].marks[idx].count / (float)timeline_maxcount); - nk_stroke_line(&win->buffer, x, rc.y + y, x, rc.y + rowheight, 1, nk_rgb(144, 144, 128)); + nk_stroke_line(&win->buffer, x, rc.y + y, x, rc.y + rowheight, 1, COLOUR_TEXT); } nk_spacing(ctx, 1); nk_layout_row_end(ctx); diff --git a/source/swotrace.h b/source/swotrace.h index d885600..eeb1471 100644 --- a/source/swotrace.h +++ b/source/swotrace.h @@ -1,7 +1,7 @@ /* * Shared code for SWO Trace for the bmtrace and bmdebug utilities. * - * Copyright 2019-2020 CompuPhase + * Copyright 2019-2022 CompuPhase * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -50,43 +50,42 @@ typedef struct tagTRACEFILTER { #define Address2Index(address, base) (((address) - (base)) / ADDRESS_ALIGN) #define Index2Address(index, base) ((index) * ADDRESS_ALIGN + (base)) - -void channel_set(int index, int enabled, const char *name, struct nk_color color); -int channel_getenabled(int index); -void channel_setenabled(int index, int enabled); +void channel_set(int index, bool enabled, const char *name, struct nk_color color); +bool channel_getenabled(int index); +void channel_setenabled(int index, bool enabled); const char *channel_getname(int index, char *name, size_t size); void channel_setname(int index, const char *name); struct nk_color channel_getcolor(int index); void channel_setcolor(int index, struct nk_color color); -int trace_init(unsigned short endpoint, const char *ipaddress); -void trace_close(void); +int trace_init(unsigned short endpoint, const char *ipaddress); +void trace_close(void); unsigned long trace_errno(int *loc); +int trace_overflowerrors(bool reset); -void trace_setdatasize(short size); -short trace_getdatasize(); -int trace_getpacketerrors(void); +void trace_setdatasize(short size); +short trace_getdatasize(); +int trace_getpacketerrors(bool reset); -void tracestring_add(unsigned channel, const unsigned char *buffer, size_t length, double timestamp); -void tracestring_clear(void); -int tracestring_isempty(void); +void tracestring_clear(void); +int tracestring_isempty(void); unsigned tracestring_count(void); -int tracestring_process(bool enabled); -int tracestring_save(const char *filename); -int tracestring_find(const char *text, int curline); -int tracestring_findtimestamp(double timestamp); +int tracestring_process(bool enabled); +int tracestring_save(const char *filename); +int tracestring_find(const char *text, int curline); +int tracestring_findtimestamp(double timestamp); -int traceprofile_process(bool enabled, unsigned *sample_map, uint32_t code_base, uint32_t code_top, unsigned *overflow); +int traceprofile_process(bool enabled, unsigned *sample_map, uint32_t code_base, uint32_t code_top, unsigned *overflow); -void tracelog_statusmsg(int type, const char *msg, int code); -void tracelog_statusclear(void); +void tracelog_statusmsg(int type, const char *msg, int code); +void tracelog_statusclear(void); const char *tracelog_getstatusmsg(int idx); -float tracelog_labelwidth(float rowheight); -void tracelog_widget(struct nk_context *ctx, const char *id, float rowheight, int markline, +float tracelog_labelwidth(float rowheight); +void tracelog_widget(struct nk_context *ctx, const char *id, float rowheight, int markline, const TRACEFILTER *filters, nk_flags widget_flags); -void timeline_getconfig(double *spacing, unsigned long *scale, unsigned long *delta); -void timeline_setconfig(double spacing, unsigned long scale, unsigned long delta); +void timeline_getconfig(double *spacing, unsigned long *scale, unsigned long *delta); +void timeline_setconfig(double spacing, unsigned long scale, unsigned long delta); double timeline_widget(struct nk_context *ctx, const char *id, float rowheight, nk_flags widget_flags); double get_timestamp(void); diff --git a/source/tracegen.c b/source/tracegen.c index aa5bbbc..5dc43d9 100644 --- a/source/tracegen.c +++ b/source/tracegen.c @@ -27,6 +27,7 @@ #include #include #include +#include "svnrev.h" #if defined __linux__ #include @@ -480,8 +481,8 @@ void generate_funcstubs(FILE *fp, unsigned flags, const char *trace_func, static void usage(int status) { - printf("tragegen - generate C source & header files from TSDL specifications," - " for tracing in the Common Trace Format.\n\n" + printf("\ntragegen - generate C source & header files from TSDL specifications, for" + " tracing in the Common Trace Format.\n\n" "Usage: tracegen [options] inputfile\n\n" "Options:\n" "-c99 Generate C99-compatible code (default is C90).\n" @@ -493,7 +494,8 @@ static void usage(int status) "-no-instr Add a \"no_instrument_function\" attribute to all generated functions.\n" "-o=name Base output filename; a .c and .h suffix is added to this name.\n" "-s SWO tracing: use channels for stream ids.\n" - "-t Force basic C types on arguments, if available.\n"); + "-t Force basic C types on arguments, if available.\n" + "-v Show version information.\n"); exit(status); } @@ -503,6 +505,13 @@ static void unknown_option(const char *option) exit(EXIT_FAILURE); } +static void version(int status) +{ + printf("tracegen version 1.1.%d.\n", SVNREV_NUM); + printf("Copyright 2019-2022 CompuPhase\nLicensed under the Apache License version 2.0\n"); + exit(status); +} + int main(int argc, char *argv[]) { PATHLIST includepaths = { NULL, NULL }, *path; @@ -510,7 +519,6 @@ int main(int argc, char *argv[]) char trace_func[64], timestamp_func[64]; char *ptr; unsigned opt_flags; - int idx; if (argc <= 1) usage(EXIT_FAILURE); @@ -521,7 +529,7 @@ int main(int argc, char *argv[]) strcpy(trace_func, "trace_xmit"); strcpy(timestamp_func, "trace_timestamp"); opt_flags = 0; - for (idx = 1; idx < argc; idx++) { + for (int idx = 1; idx < argc; idx++) { if (IS_OPTION(argv[idx])) { switch (argv[idx][1]) { case '?': @@ -590,6 +598,9 @@ int main(int argc, char *argv[]) case 't': opt_flags |= FLAG_BASICTYPES; break; + case 'v': + version(EXIT_SUCCESS); + break; default: unknown_option(argv[idx]); }