blob: 97eaed10cb584e4608496381326130964074b5e1 [file] [log] [blame]
/* Locate source files and line information for given addresses
Copyright (C) 2005 Red Hat, Inc.
Written by Ulrich Drepper <drepper@redhat.com>, 2005.
This program is Open Source software; you can redistribute it and/or
modify it under the terms of the Open Software License version 1.0 as
published by the Open Source Initiative.
You should have received a copy of the Open Software License along
with this program; if not, you may obtain a copy of the Open Software
License version 1.0 from http://www.opensource.org/licenses/osl.php or
by writing the Open Source Initiative c/o Lawrence Rosen, Esq.,
3001 King Ranch Road, Ukiah, CA 95482. */
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <argp.h>
#include <assert.h>
#include <errno.h>
#include <error.h>
#include <fcntl.h>
#include <inttypes.h>
#include <libdwfl.h>
#include <dwarf.h>
#include <libintl.h>
#include <locale.h>
#include <mcheck.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdio_ext.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
/* Name and version of program. */
static void print_version (FILE *stream, struct argp_state *state);
void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
/* Bug report address. */
const char *argp_program_bug_address = PACKAGE_BUGREPORT;
/* Values for the parameters which have no short form. */
#define OPT_DEMANGLER 0x100
/* Definitions of arguments for argp functions. */
static const struct argp_option options[] =
{
{ NULL, 0, NULL, 0, N_("Output Selection:"), 0 },
{ "basenames", 's', NULL, 0, N_("Show only base names of source files"), 0 },
{ "functions", 'f', NULL, 0, N_("Additional show function names"), 0 },
{ NULL, 0, NULL, 0, N_("Miscellaneous:"), 0 },
/* Unsupported options. */
{ "target", 'b', "ARG", OPTION_HIDDEN, NULL, 0 },
{ "demangle", 'C', "ARG", OPTION_HIDDEN | OPTION_ARG_OPTIONAL, NULL, 0 },
{ "demangler", OPT_DEMANGLER, "ARG", OPTION_HIDDEN, NULL, 0 },
{ NULL, 0, NULL, 0, NULL, 0 }
};
/* Short description of program. */
static const char doc[] = N_("\
Locate source files and line information for ADDRs (in a.out by default).");
/* Strings for arguments in help texts. */
static const char args_doc[] = N_("[ADDR...]");
/* Prototype for option handler. */
static error_t parse_opt (int key, char *arg, struct argp_state *state);
static struct argp_child argp_children[2]; /* [0] is set in main. */
/* Data structure to communicate with argp functions. */
static const struct argp argp =
{
options, parse_opt, args_doc, doc, argp_children, NULL, NULL
};
/* Handle ADDR. */
static void handle_address (GElf_Addr addr, Dwfl *dwfl);
/* True if only base names of files should be shown. */
static bool only_basenames;
/* True if function names should be shown. */
static bool show_functions;
int
main (int argc, char *argv[])
{
int remaining;
int result = 0;
/* Make memory leak detection possible. */
mtrace ();
/* We use no threads here which can interfere with handling a stream. */
(void) __fsetlocking (stdout, FSETLOCKING_BYCALLER);
/* Set locale. */
(void) setlocale (LC_ALL, "");
/* Make sure the message catalog can be found. */
(void) bindtextdomain (PACKAGE, LOCALEDIR);
/* Initialize the message catalog. */
(void) textdomain (PACKAGE);
/* Parse and process arguments. This includes opening the modules. */
argp_children[0].argp = dwfl_standard_argp ();
Dwfl *dwfl = NULL;
(void) argp_parse (&argp, argc, argv, 0, &remaining, &dwfl);
assert (dwfl != NULL);
/* Now handle the addresses. In case none are given on the command
line, read from stdin. */
if (remaining == argc)
{
/* We use no threads here which can interfere with handling a stream. */
(void) __fsetlocking (stdin, FSETLOCKING_BYCALLER);
char *buf = NULL;
size_t len = 0;
while (!feof_unlocked (stdin))
{
if (getline (&buf, &len, stdin) < 0)
break;
char *endp;
uintmax_t addr = strtoumax (buf, &endp, 0);
if (endp != buf)
handle_address (addr, dwfl);
else
result = 1;
}
free (buf);
}
else
{
do
{
char *endp;
uintmax_t addr = strtoumax (argv[remaining], &endp, 0);
if (endp != argv[remaining])
handle_address (addr, dwfl);
else
result = 1;
}
while (++remaining < argc);
}
return result;
}
/* Print the version information. */
static void
print_version (FILE *stream, struct argp_state *state __attribute__ ((unused)))
{
fprintf (stream, "addr2line (%s) %s\n", PACKAGE_NAME, VERSION);
fprintf (stream, gettext ("\
Copyright (C) %s Red Hat, Inc.\n\
This is free software; see the source for copying conditions. There is NO\n\
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
"), "2005");
fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
}
/* Handle program arguments. */
static error_t
parse_opt (int key, char *arg __attribute__ ((unused)),
struct argp_state *state)
{
switch (key)
{
case ARGP_KEY_INIT:
state->child_inputs[0] = state->input;
break;
case 'b':
case 'C':
case OPT_DEMANGLER:
/* Ignored for compatibility. */
break;
case 's':
only_basenames = true;
break;
case 'f':
show_functions = true;
break;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}
static const char *
dwarf_diename_integrate (Dwarf_Die *die)
{
Dwarf_Attribute attr_mem;
return dwarf_formstring (dwarf_attr_integrate (die, DW_AT_name, &attr_mem));
}
static bool
print_dwarf_function (Dwfl_Module *mod, Dwarf_Addr addr)
{
Dwarf_Addr bias = 0;
Dwarf_Die *cudie = dwfl_module_addrdie (mod, addr, &bias);
Dwarf_Die *scopes;
int nscopes = dwarf_getscopes (cudie, addr - bias, &scopes);
if (nscopes <= 0)
return false;
for (int i = 0; i < nscopes; ++i)
switch (dwarf_tag (&scopes[i]))
{
case DW_TAG_subprogram:
{
const char *name = dwarf_diename_integrate (&scopes[i]);
if (name == NULL)
return false;
puts (name);
return true;
}
case DW_TAG_inlined_subroutine:
{
const char *name = dwarf_diename_integrate (&scopes[i]);
if (name == NULL)
return false;
printf ("%s inlined", name);
Dwarf_Files *files;
if (dwarf_getsrcfiles (cudie, &files, NULL) == 0)
{
Dwarf_Attribute attr_mem;
Dwarf_Word val;
if (dwarf_formudata (dwarf_attr (&scopes[i],
DW_AT_call_file,
&attr_mem), &val) == 0)
{
const char *file = dwarf_filesrc (files, val, NULL, NULL);
int lineno = 0, colno = 0;
if (dwarf_formudata (dwarf_attr (&scopes[i],
DW_AT_call_line,
&attr_mem), &val) == 0)
lineno = val;
if (dwarf_formudata (dwarf_attr (&scopes[i],
DW_AT_call_column,
&attr_mem), &val) == 0)
colno = val;
if (lineno == 0)
{
if (file != NULL)
printf (" from %s", file);
}
else if (colno == 0)
printf (" at %s:%u", file, lineno);
else
printf (" at %s:%u:%u", file, lineno, colno);
}
}
printf (" in ");
continue;
}
}
return false;
}
static void
handle_address (GElf_Addr addr, Dwfl *dwfl)
{
Dwfl_Module *mod = dwfl_addrmodule (dwfl, addr);
if (show_functions)
{
/* First determine the function name. Use the DWARF information if
possible. */
if (! print_dwarf_function (mod, addr))
puts (dwfl_module_addrname (mod, addr) ?: "??");
}
Dwfl_Line *line = dwfl_module_getsrc (mod, addr);
const char *src;
int lineno, linecol;
if (line != NULL && (src = dwfl_lineinfo (line, &addr, &lineno, &linecol,
NULL, NULL)) != NULL)
{
if (only_basenames)
src = basename (src);
if (linecol != 0)
printf ("%s:%d:%d\n", src, lineno, linecol);
else
printf ("%s:%d\n", src, lineno);
}
else
puts ("??:0");
}