| /* |
| * SPDX-License-Identifier: GPL-2.0-only |
| * |
| * Copyright (C) 2007 Davi E. M. Arnaut <davi@haxent.com.br> |
| */ |
| |
| #include <argp.h> |
| #include <malloc.h> |
| #include <search.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| |
| #include "dwarves.h" |
| #include "dutil.h" |
| |
| static int verbose; |
| |
| static struct conf_fprintf conf = { |
| .emit_stats = 1, |
| }; |
| |
| static struct conf_load conf_load = { |
| .conf_fprintf = &conf, |
| }; |
| |
| struct extvar { |
| struct extvar *next; |
| const char *name; |
| const struct variable *var; |
| const struct cu *cu; |
| }; |
| |
| struct extfun { |
| struct extfun *next; |
| const char *name; |
| const struct function *fun; |
| const struct cu *cu; |
| }; |
| |
| static void *tree; |
| |
| static void oom(const char *msg) |
| { |
| fprintf(stderr, "pglobal: out of memory (%s)\n", msg); |
| exit(EXIT_FAILURE); |
| } |
| |
| static struct extvar *extvar__new(const struct variable *var, |
| const struct cu *cu) |
| { |
| struct extvar *gvar = malloc(sizeof(*gvar)); |
| |
| if (gvar != NULL) { |
| gvar->next = NULL; |
| gvar->var = var; |
| gvar->cu = cu; |
| gvar->name = variable__name(var); |
| } |
| |
| return gvar; |
| } |
| |
| static struct extfun *extfun__new(struct function *fun, |
| const struct cu *cu) |
| { |
| struct extfun *gfun = malloc(sizeof(*gfun)); |
| |
| if (gfun != NULL) { |
| gfun->next = NULL; |
| gfun->fun = fun; |
| gfun->cu = cu; |
| gfun->name = function__name(fun); |
| } |
| |
| return gfun; |
| } |
| |
| static int extvar__compare(const void *a, const void *b) |
| { |
| const struct extvar *ga = a, *gb = b; |
| return strcmp(ga->name, gb->name); |
| } |
| |
| static int extfun__compare(const void *a, const void *b) |
| { |
| const struct extfun *ga = a, *gb = b; |
| return strcmp(ga->name, gb->name); |
| } |
| |
| static void extvar__add(const struct variable *var, const struct cu *cu) |
| { |
| struct extvar **nodep, *gvar = extvar__new(var, cu); |
| |
| if (gvar != NULL) { |
| nodep = tsearch(gvar, &tree, extvar__compare); |
| if (nodep == NULL) |
| oom("tsearch"); |
| else if (*nodep != gvar) { |
| if (gvar->var->declaration) { |
| gvar->next = (*nodep)->next; |
| (*nodep)->next = gvar; |
| } else { |
| gvar->next = *nodep; |
| *nodep = gvar; |
| } |
| } |
| } |
| } |
| |
| static void extfun__add(struct function *fun, const struct cu *cu) |
| { |
| struct extfun **nodep, *gfun = extfun__new(fun, cu); |
| |
| if (gfun != NULL) { |
| nodep = tsearch(gfun, &tree, extfun__compare); |
| if (nodep == NULL) |
| oom("tsearch"); |
| else if (*nodep != gfun) { |
| gfun->next = (*nodep)->next; |
| (*nodep)->next = gfun; |
| } |
| } |
| } |
| |
| static int cu_extvar_iterator(struct cu *cu, void *cookie __maybe_unused) |
| { |
| struct tag *pos; |
| uint32_t id; |
| |
| cu__for_each_variable(cu, id, pos) { |
| struct variable *var = tag__variable(pos); |
| if (var->external) |
| extvar__add(var, cu); |
| } |
| return 0; |
| } |
| |
| static int cu_extfun_iterator(struct cu *cu, void *cookie __maybe_unused) |
| { |
| struct function *pos; |
| uint32_t id; |
| |
| cu__for_each_function(cu, id, pos) |
| if (pos->external) |
| extfun__add(pos, cu); |
| return 0; |
| } |
| |
| static inline const struct extvar *node__variable(const void *nodep) |
| { |
| return *((const struct extvar **)nodep); |
| } |
| |
| static inline const struct extfun *node__function(const void *nodep) |
| { |
| return *((const struct extfun **)nodep); |
| } |
| |
| static inline struct tag *extvar__tag(const struct extvar *gvar) |
| { |
| return (struct tag *)gvar->var; |
| } |
| |
| static inline struct tag *extfun__tag(const struct extfun *gfun) |
| { |
| return (struct tag *)gfun->fun; |
| } |
| |
| static void declaration_action__walk(const void *nodep, const VISIT which, |
| const int depth __maybe_unused) |
| { |
| uint32_t count = 0; |
| struct tag *tag; |
| const struct extvar *pos, *gvar = NULL; |
| |
| switch(which) { |
| case preorder: |
| break; |
| case postorder: |
| gvar = node__variable(nodep); |
| break; |
| case endorder: |
| break; |
| case leaf: |
| gvar = node__variable(nodep); |
| break; |
| } |
| |
| if (gvar == NULL) |
| return; |
| |
| tag = extvar__tag(gvar); |
| |
| tag__fprintf(tag, gvar->cu, NULL, stdout); |
| |
| for (pos = gvar->next; pos; pos = pos->next) |
| count++; |
| |
| printf("; /* %u */\n\n", count); |
| } |
| |
| static void function_action__walk(const void *nodep, const VISIT which, |
| const int depth __maybe_unused) |
| { |
| struct tag *tag; |
| const struct extfun *gfun = NULL; |
| |
| switch(which) { |
| case preorder: |
| break; |
| case postorder: |
| gfun = node__function(nodep); |
| break; |
| case endorder: |
| break; |
| case leaf: |
| gfun = node__function(nodep); |
| break; |
| } |
| |
| if (gfun == NULL) |
| return; |
| |
| tag = extfun__tag(gfun); |
| |
| tag__fprintf(tag, gfun->cu, NULL, stdout); |
| |
| fputs("\n\n", stdout); |
| } |
| |
| static void free_node(void *nodep) |
| { |
| void **node = nodep; |
| free(*node); |
| } |
| |
| /* Name and version of program. */ |
| ARGP_PROGRAM_VERSION_HOOK_DEF = dwarves_print_version; |
| |
| static const struct argp_option pglobal__options[] = { |
| { |
| .key = 'v', |
| .name = "variables", |
| .doc = "show global variables", |
| }, |
| { |
| .key = 'f', |
| .name = "functions", |
| .doc = "show global functions", |
| }, |
| { |
| .name = "format_path", |
| .key = 'F', |
| .arg = "FORMAT_LIST", |
| .doc = "List of debugging formats to try" |
| }, |
| { |
| .key = 'V', |
| .name = "verbose", |
| .doc = "be verbose", |
| }, |
| { |
| .name = NULL, |
| } |
| }; |
| |
| static int walk_var, walk_fun; |
| |
| static error_t pglobal__options_parser(int key, char *arg __maybe_unused, |
| struct argp_state *state) |
| { |
| switch (key) { |
| case ARGP_KEY_INIT: |
| if (state->child_inputs != NULL) |
| state->child_inputs[0] = state->input; |
| break; |
| case 'v': walk_var = 1; break; |
| case 'f': walk_fun = 1; break; |
| case 'V': verbose = 1; break; |
| case 'F': conf_load.format_path = arg; break; |
| default: return ARGP_ERR_UNKNOWN; |
| } |
| return 0; |
| } |
| |
| static const char pglobal__args_doc[] = "FILE"; |
| |
| static struct argp pglobal__argp = { |
| .options = pglobal__options, |
| .parser = pglobal__options_parser, |
| .args_doc = pglobal__args_doc, |
| }; |
| |
| int main(int argc, char *argv[]) |
| { |
| int err, remaining, rc = EXIT_FAILURE; |
| |
| if (argp_parse(&pglobal__argp, argc, argv, 0, &remaining, NULL) || |
| remaining == argc) { |
| argp_help(&pglobal__argp, stderr, ARGP_HELP_SEE, argv[0]); |
| goto out; |
| } |
| |
| if (dwarves__init()) { |
| fputs("pglobal: insufficient memory\n", stderr); |
| goto out; |
| } |
| |
| dwarves__resolve_cacheline_size(&conf_load, 0); |
| |
| struct cus *cus = cus__new(); |
| if (cus == NULL) { |
| fputs("pglobal: insufficient memory\n", stderr); |
| goto out_dwarves_exit; |
| } |
| |
| err = cus__load_files(cus, &conf_load, argv + remaining); |
| if (err != 0) { |
| cus__fprintf_load_files_err(cus, "pglobal", argv + remaining, err, stderr); |
| goto out_cus_delete; |
| } |
| |
| if (walk_var) { |
| cus__for_each_cu(cus, cu_extvar_iterator, NULL, NULL); |
| twalk(tree, declaration_action__walk); |
| } else if (walk_fun) { |
| cus__for_each_cu(cus, cu_extfun_iterator, NULL, NULL); |
| twalk(tree, function_action__walk); |
| } |
| |
| tdestroy(tree, free_node); |
| rc = EXIT_SUCCESS; |
| out_cus_delete: |
| cus__delete(cus); |
| out_dwarves_exit: |
| dwarves__exit(); |
| out: |
| return rc; |
| } |