aboutsummaryrefslogtreecommitdiff
path: root/instrument-functions.c
diff options
context:
space:
mode:
authorFrancois-Xavier Le Bail <devel.fx.lebail@orange.fr>2023-02-28 16:34:36 +0100
committerFrancois-Xavier Le Bail <devel.fx.lebail@orange.fr>2023-02-28 16:45:03 +0100
commitce5a3974526c90a26ba83d338826e2720807bdc2 (patch)
treecc72fec467e3b7819e5e0effa245db1e7058ce58 /instrument-functions.c
parent9ada8cb0c32a1535a8a16606588e9cc9db1d7140 (diff)
Add instrument-functions.c to help debugging/learning processes
This is the same code as in tcpdump and tcpslice. Documentation on how to use it will be added later.
Diffstat (limited to 'instrument-functions.c')
-rw-r--r--instrument-functions.c239
1 files changed, 239 insertions, 0 deletions
diff --git a/instrument-functions.c b/instrument-functions.c
new file mode 100644
index 00000000..fb2314dd
--- /dev/null
+++ b/instrument-functions.c
@@ -0,0 +1,239 @@
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <bfd.h>
+
+/*
+ * Generate instrumentation calls for entry and exit to functions.
+ * Just after function entry and just before function exit, the
+ * following profiling functions are called with the address of the
+ * current function and its call site (currently not use).
+ *
+ * The attribute 'no_instrument_function' causes this instrumentation is
+ * not done.
+ *
+ * These profiling functions call print_debug(). This function prints the
+ * current function name with indentation and call level.
+ * If entering in a function it prints also the calling function name with
+ * file name and line number.
+ *
+ * To configure the printing of only the global functions names:
+ * $ make instrument_global
+ *
+ * To go back to print all the functions names:
+ * $ make instrument_all
+ *
+ * To print nothing, like with no instrumentation:
+ * $ make instrument_off
+ */
+
+#define ND_NO_INSTRUMENT __attribute__((no_instrument_function))
+
+/* Store the function call level, used also in pretty_print_packet() */
+extern int profile_func_level;
+int profile_func_level = -1;
+
+typedef enum {
+ ENTER,
+ EXIT
+} action_type;
+
+void __cyg_profile_func_enter(void *this_fn, void *call_site) ND_NO_INSTRUMENT;
+
+void __cyg_profile_func_exit(void *this_fn, void *call_site) ND_NO_INSTRUMENT;
+
+static void print_debug(void *this_fn, void *call_site, action_type action)
+ ND_NO_INSTRUMENT;
+
+void
+__cyg_profile_func_enter(void *this_fn, void *call_site)
+{
+ print_debug(this_fn, call_site, ENTER);
+}
+
+void
+__cyg_profile_func_exit(void *this_fn, void *call_site)
+{
+ print_debug(this_fn, call_site, EXIT);
+}
+
+/* If this file exists, print only the global functions */
+#define ND_FILE_FLAG_GLOBAL "instrument_functions_global.devel"
+
+/* If this file exists, print nothing, like with no instrumentation */
+#define ND_FILE_FLAG_OFF "instrument_functions_off.devel"
+
+static void print_debug(void *this_fn, void *call_site, action_type action)
+{
+ static bfd* abfd;
+ static asymbol **symtab;
+ static long symcount;
+ static asection *text;
+ static bfd_vma vma;
+ static int instrument_off;
+ static int print_only_global;
+ symbol_info syminfo;
+ struct stat statbuf;
+ int i;
+
+ if (!instrument_off) {
+ /* one-time test */
+ if (!stat(ND_FILE_FLAG_OFF, &statbuf)) {
+ instrument_off = 1;
+ return;
+ }
+ } else
+ return;
+
+ /* If no errors, this block should be executed one time */
+ if (!abfd) {
+ char pgm_name[1024];
+ long symsize;
+
+ if (!stat(ND_FILE_FLAG_GLOBAL, &statbuf))
+ print_only_global = 1;
+
+ ssize_t ret = readlink("/proc/self/exe", pgm_name, sizeof(pgm_name));
+ if (ret == -1) {
+ perror("failed to find executable\n");
+ return;
+ }
+ pgm_name[ret] = 0;
+
+ bfd_init();
+
+ abfd = bfd_openr(pgm_name, NULL);
+ if (!abfd) {
+ bfd_perror("bfd_openr");
+ return;
+ }
+
+ if (!bfd_check_format(abfd, bfd_object)) {
+ bfd_perror("bfd_check_format");
+ return;
+ }
+
+ if((symsize = bfd_get_symtab_upper_bound(abfd)) == -1) {
+ bfd_perror("bfd_get_symtab_upper_bound");
+ return;
+ }
+
+ symtab = (asymbol **)malloc(symsize);
+ symcount = bfd_canonicalize_symtab(abfd, symtab);
+ if (symcount < 0) {
+ free (symtab);
+ bfd_perror ("bfd_canonicalize_symtab");
+ return;
+ }
+
+ if ((text = bfd_get_section_by_name(abfd, ".text")) == NULL) {
+ bfd_perror("bfd_get_section_by_name");
+ return;
+ }
+ vma = text->vma;
+ }
+
+ if (print_only_global) {
+ int found;
+
+ i = 0;
+ found = 0;
+ while (i < symcount && !found) {
+ bfd_get_symbol_info(abfd, symtab[i], &syminfo);
+ if ((void *)syminfo.value == this_fn) {
+ found = 1;
+ }
+ i++;
+ }
+ /* type == 'T' for a global function */
+ if (found == 1 && syminfo.type != 'T')
+ return;
+ }
+
+ /* Current function */
+ if ((bfd_vma)this_fn < vma) {
+ printf("[ERROR address this_fn]");
+ } else {
+ const char *file;
+ const char *func;
+ unsigned int line;
+
+ if (!bfd_find_nearest_line(abfd, text, symtab, (bfd_vma)this_fn - vma,
+ &file, &func, &line)) {
+ printf("[ERROR bfd_find_nearest_line this_fn]");
+ } else {
+ if (action == ENTER)
+ profile_func_level += 1;
+ /* Indentation */
+ for (i = 0 ; i < profile_func_level ; i++)
+ putchar(' ');
+ if (action == ENTER)
+ printf("[>> ");
+ else
+ printf("[<< ");
+ /* Function name */
+ if (func == NULL || *func == '\0')
+ printf("???");
+ else
+ printf("%s", func);
+ printf(" (%d)", profile_func_level);
+ /* Print the "from" part except for the main function) */
+ if (action == ENTER && strncmp(func, "main", sizeof("main"))) {
+ /* Calling function */
+ if ((bfd_vma)call_site < vma) {
+ printf("[ERROR address call_site]");
+ } else {
+ if (!bfd_find_nearest_line(abfd, text, symtab,
+ (bfd_vma)call_site - vma, &file,
+ &func, &line)) {
+ printf("[ERROR bfd_find_nearest_line call_site]");
+ } else {
+ printf(" from ");
+ /* Function name */
+ if (func == NULL || *func == '\0')
+ printf("???");
+ else
+ printf("%s", func);
+ /* File name */
+ if (file == NULL || *file == '\0')
+ printf(" ??:");
+ else {
+ char *slashp = strrchr(file, '/');
+ if (slashp != NULL)
+ file = slashp + 1;
+ printf(" %s:", file);
+ }
+ /* Line number */
+ if (line == 0)
+ printf("?");
+ else
+ printf("%u", line);
+ printf("]");
+ }
+ }
+ }
+ putchar('\n');
+ if (action == EXIT)
+ profile_func_level -= 1;
+ }
+ }
+ fflush(stdout);
+}
+
+/* vi: set tabstop=4 softtabstop=0 shiftwidth=4 smarttab autoindent : */