Skip to content

Instantly share code, notes, and snippets.

@Toliak
Last active March 4, 2025 10:13
Show Gist options
  • Save Toliak/b4d5422dd7d436ded4ae321ce1e73814 to your computer and use it in GitHub Desktop.
Save Toliak/b4d5422dd7d436ded4ae321ce1e73814 to your computer and use it in GitHub Desktop.
Better logs (or just different) for Callgrind

Description

Patch for Valgrind commit 2d09ef48e2. Also works on tag VALGRIND_3_24_0

Provides logs for callgrind in the format:

  • Function entering
+ <Stack position> [<Module name>] <Function name> (<source line of the beginning of the called function, if debug symbols provided>)
  • Function exiting
- <Stack position (before exit)> [<Module name>] <Function name> (<source line the function will return to, if debug symbols provided>)

Also provides two arguments: --ct-verbose-suppress-module and --ct-verbose-suppress-function. The can be used to specify modules and functions for supress, respectively. Multiple module or function names should be separated by a colon.

Example

Consider C code:

main.c
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>


// Function declarations
int f1(int v);
int f2(int v);
int f3(int v);
int f4(int v);
int f5(int v);
int f6(int v);
int f7(int v);
int f8(int v);
int f9(int v);
int f10(int v);

void file_check(void) {
    int fd = open("/dev/null", O_RDONLY);
    if (fd == -1) {
        perror("open");
        exit(1);
    }

    char buffer[1024];
    read(fd, buffer, sizeof(buffer));

    close(fd);
}

// Function definitions
int f1(int v) {
    return f2(v + 1);
}

int f2(int v) {
    file_check();

    if (v > 10) {
        return (v - 5);
    }
    return f3(v * 2);
}

int f3(int v) {
    return f8(v + 3);
}

int f4(int v) {
    if (v < 0) {
        return (v + 10);
    }

    return f6(v - 2);
}

int f5(int v) {
    return f9(v / 2);
}

int f6(int v) {
    if (v % 2 == 0) {
        return (v + 1);
    }
    return f4(v - 1);
}

int f7(int v) {
    return f10(v * 3);
}

int f8(int v) {
    if (v > 20) {
        return (v - 10);
    }
    return f6(v + 5);
}

int f9(int v) {
    return f3(v - 4);
}

int f10(int v) {
    if (v == 0) {
        return 0; // Base case to stop recursion
    }
    return f2(v - 1);
}

int main(int argc, char** argv) {
    int a = 3;
    printf("Result: %d\n", f1(a));
}

Command to build the code above:

gcc main.c -O0 -g -o build/app

Command to run callgrind:

$MY_VALGRIND_DIR/dist/bin/valgrind --tool=callgrind --ct-verbose=1 --ct-verbose-suppress-module="ld-linux-x86-64.so.2" --ct-verbose-suppress-function="__syscall_cancel:__internal_syscall_cancel" --log-file=./log.txt ./build/app

The log output (cut):

+  4 000000000010938e [app] main (/my-project-directory/main.c:91)
+  5 0000000000109234 [app] f1 (/my-project-directory/main.c:33)
+  6 000000000010924e [app] f2 (/my-project-directory/main.c:37)
+  7 0000000000109199 [app] file_check (/my-project-directory/main.c:19)
+  9 00000000049832d0 [libc.so.6] open (/usr/src/debug/glibc/glibc/io/../sysdeps/unix/sysv/linux/open64.c:30)
-  9 00000000049832d0 [libc.so.6] open (/my-project-directory/main.c:20)
+  9 0000000004983a20 [libc.so.6] read (/usr/src/debug/glibc/glibc/io/../sysdeps/unix/sysv/linux/read.c:25)
-  9 0000000004983a20 [libc.so.6] read (/my-project-directory/main.c:27)
+  9 000000000497ee60 [libc.so.6] close (/usr/src/debug/glibc/glibc/io/../sysdeps/unix/sysv/linux/close.c:26)
-  9 000000000497ee60 [libc.so.6] close (/my-project-directory/main.c:29)
-  7 0000000000109199 [app] file_check (/my-project-directory/main.c:38)
+  7 000000000010927a [app] f3 (/my-project-directory/main.c:46)
+  8 0000000000109325 [app] f8 (/my-project-directory/main.c:73)
+  9 00000000001092dc [app] f6 (/my-project-directory/main.c:62)
-  9 00000000001092dc [app] f6 (/my-project-directory/main.c:77)
-  8 0000000000109325 [app] f8 (/my-project-directory/main.c:47)
-  7 000000000010927a [app] f3 (/my-project-directory/main.c:43)
-  6 000000000010924e [app] f2 (/my-project-directory/main.c:34)
-  5 0000000000109234 [app] f1 (/my-project-directory/main.c:93)
+  6 00000000048d0e00 [libc.so.6] printf (/usr/src/debug/glibc/glibc/stdio-common/printf.c:28)
diff --git a/callgrind/callstack.c b/callgrind/callstack.c
index fc1d6eeab..acc7c5542 100644
--- a/callgrind/callstack.c
+++ b/callgrind/callstack.c
@@ -174,6 +174,135 @@ static void function_left(fn_node* fn)
}
+/* Returns uhhhh the same pointer to the fn_buf. If no fn_buf (or it is "???") -- `fallback_fn_name`
+ See debug.c, `CLG_(print_addr)` for the reference
+*/
+static HChar* CLG_(get_fn_buf_name)(HChar* fn_buf) {
+ const HChar* fallback_fn_name = "???";
+ if (!fn_buf || VG_(strcmp)(fn_buf,"???") == 0) {
+ return fallback_fn_name;
+ }
+ return fn_buf;
+}
+
+
+/* Returns shifted pointer to the same allocated string. If no module name -- `fallback_obj_name`
+ See debug.c, `CLG_(print_addr)` for the reference
+*/
+static HChar* CLG_(get_di_obj_name)(DebugInfo *di) {
+ const HChar* fallback_obj_name = "???";
+ UInt i=0, opos=0;
+
+ if (UNLIKELY(!di)) {
+ return fallback_obj_name;
+ }
+
+ const HChar* obj_name = VG_(DebugInfo_get_filename)(di);
+ if (obj_name) {
+ while(obj_name[i]) {
+ if (obj_name[i]=='/') {
+ opos = i+1;
+ }
+ i++;
+ }
+
+ obj_name += opos;
+ return (obj_name[0]) ? obj_name : fallback_obj_name;
+ }
+
+ return fallback_obj_name;
+}
+
+
+/* Prints source mapped code line
+ See debug.c, `CLG_(print_addr)` for the reference
+*/
+static void CLG_(print_fl_buf_source_line)(UInt di_ln, const HChar* dir_buf, const HChar* fl_buf) {
+ if (di_ln>0) {
+ if (dir_buf[0]) {
+ VG_(printf)("(%s/%s:%u)", dir_buf, fl_buf, di_ln);}
+ else {
+ VG_(printf)("(%s:%u)", fl_buf, di_ln);
+ }
+ } else {
+ VG_(printf)("(???)");
+ }
+}
+
+
+static void CLG_(print_verbose_1_patch)(jCC* jcc, Bool with_jmpaddr) {
+ const HChar *fn_buf, *fl_buf, *dir_buf;
+ const HChar *fn_name, *obj_name;
+ DebugInfo* di;
+ UInt di_ln;
+ Addr addr, jmpaddr;
+ Bool is_blacklisted = False;
+
+ if (jcc && jcc->to && jcc->to->bb) {
+ // Do not print or even process if there is no jcc
+
+ addr = bb_addr(jcc->to->bb);
+ jmpaddr = bb_jmpaddr(jcc->from->bb); // TODO: may be bb_addr(jcc->from-bb) useful?
+
+ if (addr != 0) {
+ CLG_(get_debug_info)(addr, &dir_buf, &fl_buf, &fn_buf, &di_ln, &di);
+
+ fn_name = CLG_(get_fn_buf_name)(fn_buf);
+ obj_name = CLG_(get_di_obj_name)(di);
+
+ for (SizeT i = 0; i < VG_(sizeXA)(CLG_(clo).suppress_module) && !is_blacklisted; i++) {
+ const HChar *module = *(HChar**)VG_(indexXA)(CLG_(clo).suppress_module, i);
+ if (VG_(strncmp)(module, obj_name, 1024) == 0) {
+ // VG_(printf)("[B] %s blacklisted by %s\n", obj_name, module);
+ is_blacklisted = True;
+ break;
+ }
+ }
+ for (SizeT i = 0; i < VG_(sizeXA)(CLG_(clo).suppress_function) && !is_blacklisted; i++) {
+ const HChar *function = *(HChar**)VG_(indexXA)(CLG_(clo).suppress_function, i);
+ if (VG_(strncmp)(function, fn_name, 1024) == 0) {
+ // VG_(printf)("[B] %s blacklisted by %s\n", obj_name, function);
+ is_blacklisted = True;
+ break;
+ }
+ }
+ }
+
+ if (!is_blacklisted) {
+ if (with_jmpaddr) {
+ VG_(printf)("- %2d ", CLG_(current_call_stack).sp + 1);
+ }
+ else {
+ VG_(printf)("+ %2d ", CLG_(current_call_stack).sp);
+ }
+
+ VG_(printf)("%016llx ", addr);
+
+ if (addr != 0) {
+ VG_(printf)("[%s] %s ", obj_name, fn_name);
+
+ if (with_jmpaddr) {
+ if (jmpaddr != 0) {
+ CLG_(get_debug_info)(jmpaddr, &dir_buf, &fl_buf, &fn_buf, &di_ln, &di);
+ CLG_(print_fl_buf_source_line)(di_ln, dir_buf, fl_buf);
+ } else {
+ VG_(printf)("(???)");
+ }
+ }
+ else {
+ CLG_(print_fl_buf_source_line)(di_ln, dir_buf, fl_buf);
+ }
+ }
+ else {
+ VG_(printf)("NO_DI");
+ }
+
+ VG_(printf)("\n");
+ }
+ }
+}
+
+
/* Push call on call stack.
*
* Increment the usage count for the function called.
@@ -270,24 +399,7 @@ void CLG_(push_call_stack)(BBCC* from, UInt jmp, BBCC* to, Addr sp, Bool skip)
#if CLG_ENABLE_DEBUG
CLG_DEBUGIF(0) {
if (CLG_(clo).verbose<2) {
- if (jcc && jcc->to && jcc->to->bb) {
- const HChar spaces[][41] = {
- " . . . . . . . . . .",
- " . . . . . . . . . . ",
- " . . . . . . . . . . ",
- ". . . . . . . . . . " };
-
- int s = CLG_(current_call_stack).sp;
- UInt* pars = (UInt*) sp;
-
- BB* bb = jcc->to->bb;
- if (s>40) s=40;
- VG_(printf)("%s> %s(0x%x, 0x%x, ...) [%s / %#lx]\n", spaces[s%4]+40-s, bb->fn->name,
- pars ? pars[1]:0,
- pars ? pars[2]:0,
- bb->obj->name + bb->obj->last_slash_pos,
- (UWord)bb->offset);
- }
+ CLG_(print_verbose_1_patch)(jcc, False);
}
else if (CLG_(clo).verbose<4) {
VG_(printf)("+ %2d ", CLG_(current_call_stack).sp);
@@ -368,8 +480,11 @@ void CLG_(pop_call_stack)(void)
CLG_(current_call_stack).sp--;
#if CLG_ENABLE_DEBUG
- CLG_DEBUGIF(1) {
- if (CLG_(clo).verbose<4) {
+ CLG_DEBUGIF(0) {
+ if (CLG_(clo).verbose<2) {
+ CLG_(print_verbose_1_patch)(jcc, True);
+ }
+ else if (CLG_(clo).verbose<4) {
if (jcc) {
/* popped JCC target first */
VG_(printf)("- %2d %#lx => ",
diff --git a/callgrind/clo.c b/callgrind/clo.c
index ce15fc832..7781bf2fd 100644
--- a/callgrind/clo.c
+++ b/callgrind/clo.c
@@ -399,6 +399,38 @@ void CLG_(update_fn_config)(fn_node* fn)
update_fn_config2(fn, fn->name, fn_configs);
}
+/* String split into XArray */
+static void CLG_(string_split_to_xarray)(const HChar *orig_str, HChar delim, XArray *out_array) {
+ SizeT j = 0;
+ SizeT i = 0;
+ for (i = 0; i < 16 * 1024 * 1024 && orig_str[i] != '\0'; ++i) {
+ HChar c = orig_str[i];
+ if (c == delim) {
+ if (i > j) {
+ HChar* new_str = VG_(malloc)("ct_verbose_suppress_module", i - j + 1);
+ VG_(strncpy)(new_str, orig_str + j, i - j);
+ new_str[i-j] = 0;
+ VG_(addToXA)(out_array, (void*)&new_str);
+ }
+ // Do not capture empty string
+
+ j = i+1;
+ }
+ }
+ if (i > j) {
+ HChar* new_str = VG_(malloc)("ct_verbose_suppress_module", i - j + 1);
+ VG_(strncpy)(new_str, orig_str + j, i - j);
+ new_str[i-j] = 0;
+ VG_(printf)("before addToXA2: (%d) %s\n", i-j, new_str);
+
+ VG_(addToXA)(out_array, (void*)&new_str);
+ }
+
+ // for (SizeT i = 0; i < VG_(sizeXA)(out_array); i++) {
+ // VG_(printf)("Suppressed modules: %s\n", VG_(indexXA)(out_array, i));
+ // }
+}
+
/*--------------------------------------------------------------------*/
/*--- Command line processing ---*/
@@ -466,6 +498,21 @@ Bool CLG_(process_cmd_line_option)(const HChar* arg)
else if VG_INT_CLO(arg, "--ct-verbose", CLG_(clo).verbose) {}
else if VG_INT_CLO(arg, "--ct-vstart", CLG_(clo).verbose_start) {}
+ else if VG_STR_CLO(arg, "--ct-verbose-suppress-module", tmp_str) {
+ CLG_(string_split_to_xarray)(tmp_str, ':', CLG_(clo).suppress_module);
+
+ for (SizeT i = 0; i < VG_(sizeXA)(CLG_(clo).suppress_module); i++) {
+ VG_(printf)("Suppressed modules: %s\n", *(HChar**)VG_(indexXA)(CLG_(clo).suppress_module, i));
+ }
+ }
+ else if VG_STR_CLO(arg, "--ct-verbose-suppress-function", tmp_str) {
+ CLG_(string_split_to_xarray)(tmp_str, ':', CLG_(clo).suppress_function);
+
+ for (SizeT i = 0; i < VG_(sizeXA)(CLG_(clo).suppress_function); i++) {
+ VG_(printf)("Suppressed functions: %s\n", *(HChar**)VG_(indexXA)(CLG_(clo).suppress_function, i));
+ }
+ }
+
else if VG_STREQN(12, arg, "--ct-verbose") {
fn_config* fnc;
HChar* s;
@@ -634,6 +681,8 @@ void CLG_(print_debug_usage)(void)
" --ct-verbose=<level> Verbosity of standard debug output [0]\n"
" --ct-vstart=<BB number> Only be verbose after basic block [0]\n"
" --ct-verbose<level>=<func> Verbosity while in <func>\n"
+" --ct-verbose-suppress-module=<modules> Suppress output for modules (separated by colon string)\n"
+" --ct-verbose-suppress-function=<functions> Suppress output for functions (separated by colon string)\n"
#else
" (none)\n"
#endif
@@ -685,5 +734,9 @@ void CLG_(set_clo_defaults)(void)
#if CLG_ENABLE_DEBUG
CLG_(clo).verbose = 0;
CLG_(clo).verbose_start = 0;
+ CLG_(clo).suppress_function = VG_(newXA)(VG_(malloc), "CLG(set_clo_defaults).sf",
+ VG_(free), sizeof(HChar *));
+ CLG_(clo).suppress_module= VG_(newXA)(VG_(malloc), "CLG(set_clo_defaults).sm",
+ VG_(free), sizeof(HChar *));
#endif
}
diff --git a/callgrind/global.h b/callgrind/global.h
index 4a988a481..4207e865c 100644
--- a/callgrind/global.h
+++ b/callgrind/global.h
@@ -123,6 +123,8 @@ struct _CommandLineOptions {
#if CLG_ENABLE_DEBUG
Int verbose;
ULong verbose_start;
+ XArray *suppress_module; // array of string
+ XArray *suppress_function; // array of string
#endif
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment