├── .gitignore ├── README ├── example ├── Makefile └── crumble.c ├── example2 ├── Makefile ├── crumble.c └── crumble.h └── src ├── etrace ├── etrace.pl └── ptrace.c /.gitignore: -------------------------------------------------------------------------------- 1 | crumble 2 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | 2 | etrace: A run-time tracing tool 3 | ------------------------------- 4 | These source files comprise a simple but efficient utility that allows the 5 | display of a function call tree in a C program at run-time. It is also 6 | possible to dump the results of a program execution to an ASCII file for 7 | later examination. 8 | 9 | * ptrace.c compiles your code to enable this run-time tracing 10 | * etrace is a Python script that will perform the run-time tracing by 11 | displaying the function names as they are called 12 | * etrace.pl is a Perl script that will perform the run-time tracing by 13 | displaying the function names as they are called. Moreover, etrace.pl 14 | can be used to trace dynamic libraries. 15 | 16 | License 17 | ------- 18 | This is free software placed in the public domain. Enjoy! 19 | 20 | 21 | Pre-requisites 22 | -------------- 23 | - gcc version 2.95 or later. 24 | - The presence of the 'nm' utility on your machine, with a compatible BSD 25 | output. 26 | - Python 1.5 or later (for etrace) or Perl (for etrace.pl). 27 | 28 | 29 | Portability 30 | ----------- 31 | This code is known to run under Linux, FreeBSD, Solaris, and HPUX. 32 | 33 | 34 | Example using etrace 35 | -------------------- 36 | There is an example in the example/ sub-dir. To compile it, do: 37 | 38 | % cd example 39 | % make 40 | 41 | You should now have an executable called "crumble". Running it should give 42 | you a recipe for an apple crumble, with 1-second pauses between sentences 43 | to demonstrate the run-time ability. Now let's get to this function call 44 | tree: 45 | 46 | To get the tree at run-time, open two terminals. 47 | In the first terminal: 48 | 49 | % cd etrace/example 50 | % ../src/etrace crumble 51 | 52 | In the second terminal: 53 | 54 | % cd etrace/example 55 | % crumble 56 | 57 | Both terminals need to be in the same directory. Data are exchanged between 58 | the running process you are trying to monitor and the 'etrace' script 59 | through a named pipe in this directory. 60 | 61 | You should see this: 62 | 63 | main 64 | | Crumble_make_apple_crumble 65 | | | Crumble_buy_stuff 66 | | | | Crumble_buy 67 | | | | Crumble_buy (total: 5 times) 68 | | | Crumble_prepare_apples 69 | | | | Crumble_skin_and_dice 70 | | | Crumble_mix 71 | | | Crumble_finalize 72 | | | | Crumble_put 73 | | | | Crumble_put (total: 2 times) 74 | | | Crumble_cook 75 | | | | Crumble_put 76 | | | | Crumble_bake 77 | 78 | 79 | To get the tree into a file for later use, do the following: 80 | 81 | % cd etrace/example 82 | % touch TRACE 83 | % crumble 84 | % ../src/etrace crumble > log 85 | 86 | The trace has been saved to a file called 'log' in the current directory. 87 | 88 | 89 | Example using etrace.pl 90 | ----------------------- 91 | There is an example in the example/ sub-dir. To compile it, do: 92 | 93 | % cd example 94 | % make 95 | 96 | You should now have an executable called "crumble". Running it should give 97 | you a recipe for an apple crumble, with 1-second pauses between sentences 98 | to demonstrate the run-time ability. Now let's get to this function call 99 | tree: 100 | 101 | To get the tree at run-time, open two terminals. 102 | In the first terminal: 103 | 104 | % cd etrace/example 105 | % ../src/etrace.pl crumble 106 | 107 | In the second terminal: 108 | 109 | % cd etrace/example 110 | % crumble 111 | 112 | Both terminals need to be in the same directory. Data are exchanged between 113 | the running process you are trying to monitor and the 'etrace' script 114 | through a named pipe in this directory. 115 | 116 | You should see this: 117 | 118 | \-- main 119 | | \-- Crumble_make_apple_crumble 120 | | | \-- Crumble_buy_stuff 121 | | | | \-- Crumble_buy 122 | | | | \-- Crumble_buy 123 | | | | \-- Crumble_buy 124 | | | | \-- Crumble_buy 125 | | | | \-- Crumble_buy 126 | | | \-- Crumble_prepare_apples 127 | | | | \-- Crumble_skin_and_dice 128 | | | \-- Crumble_mix 129 | | | \-- Crumble_finalize 130 | | | | \-- Crumble_put 131 | | | | \-- Crumble_put 132 | | | \-- Crumble_cook 133 | | | | \-- Crumble_put 134 | | | | \-- Crumble_bake 135 | 136 | 137 | To get the tree into a file for later use, you can do the following: 138 | 139 | % cd etrace/example 140 | % touch TRACE 141 | % crumble 142 | % ../src/etrace.pl crumble > log 143 | 144 | or 145 | 146 | % cd etrace/example 147 | % touch TRACE 148 | % crumble 149 | % ../src/etrace.pl crumble TRACE > log 150 | 151 | Either way, the trace has been saved to a file called 'log' in the 152 | current directory. 153 | 154 | To see how to modify ptrace.c to work with a dynamic library, look at 155 | the example2 directory. The sources there also create a stand-alone 156 | executable, but the PTRACE_REFERENCE_FUNCTION macro is defined just as 157 | it would be for a dynamic library. 158 | 159 | How does it work? 160 | ----------------- 161 | gcc 2.95 has a nifty feature called "instrument-functions", that adds a 162 | couple of function calls to all functions in your code. Every time a 163 | function starts, a function called __cyg_profile_func_enter() is called, 164 | and every time a function exits, a function called 165 | __cyg_profile_func_exit() is called. 166 | 167 | The code in ptrace.c simply redirects the information gathered at each call 168 | to a text file. 169 | 170 | Now, if you examine closely what you get from this output, you will see 171 | that only the binary addresses of functions are printed out. To make the 172 | link to true function names, you need to interpret the binary and make the 173 | list of associations. There are many ways of doing this, and to be truly 174 | platform-independent you should have a look at the BFD library from the GNU 175 | project. 176 | 177 | etrace implements a much simpler (but of course much less portable) 178 | solution by using the 'nm' utility to dump a list of all defined symbols 179 | and associated addresses. The link between them and the pretty printing as 180 | a function call tree is all done in the 'etrace' script. 181 | 182 | etrace.pl works in a manner similarly to etrace. Up to output 183 | pretty-printing, it works the same as etrace on stand-alone 184 | programs. If the first line of the TRACE file is marked "REFERENCE", 185 | however, it uses the values provided to calculate where the reference 186 | function actually resides in memory, and compares the difference 187 | between that address and the address in the symbol table; this offset 188 | is then applied to all the function addresses in the trace. This 189 | feature is enabled by defining the PTRACE_REFERENCE_FUNCTION in 190 | ptrace.c and is essential to correctly map the function names when 191 | tracing a dynamic library (this feature is optional for stand-alone 192 | programs). 193 | 194 | 195 | 196 | ptrace/etrace usage 197 | ------------------- 198 | The ptrace.c module will look for a file called "TRACE" in the current 199 | directory. If no such file can be found, nothing will be logged. You still 200 | get the penalty associated to the two function calls but nothing else 201 | changes. 202 | 203 | If a TRACE file can be found, log data will be appended to it. 204 | 205 | The idea is to make the TRACE file a fifo so that another process can read 206 | from it and display the informations at run-time. This is exactly what the 207 | 'etrace' script does. You do not have to create the fifo yourself, one will 208 | be created upon startup of 'etrace', and will be deleted afterwards. 209 | 210 | 'etrace' needs to know the name of the executable you are trying to trace, 211 | to retrieve the symbols (with 'nm'). This is the only argument to the 212 | script. 213 | 214 | 'etrace.pl' takes two arguments. The first argument, which is 215 | mandatory, is either the name of the executable you want to trace, or 216 | the name of the symbol file (produced by nm) of the executable you 217 | want to trace. The second argument, which is optional, is either the 218 | name of the trace file produced by ptrace (if you want to see the 219 | function calls after the program has finished running) or the name to 220 | use for the FIFO (in which case you will need to make sure ptrace.c 221 | writes to a file with that same name). 222 | 223 | 224 | How to use it in your code 225 | -------------------------- 226 | 227 | To trace a stand-alone program, there is no need to #include any 228 | additional file. Just link your code against ptrace.c and use the 229 | -finstrument-functions option as a compile option for gcc. This should 230 | do it. 231 | 232 | Now when you want to trace at run-time (handy to see where the code breaks 233 | in case of segfault), open two terminals. Launch first 'etrace myprogram' 234 | in one terminal, and 'myprogram' in the other. Remember both need to be in 235 | the same directory for the magic to work. 236 | 237 | If you want to log the trace to a file, create an empty file called TRACE 238 | in the current directory, launch your program, and examine the results 239 | using 'etrace myprogram'. 240 | 241 | If you want to trace a library, you need to provide ptrace.c with the 242 | name of a reference function. The name and address of this function, 243 | once loaded, will be the first line output to the trace file, and will 244 | be used by etrace.pl to correctly map the function pointers to the 245 | symbol names. To provide ptrace.c with the reference function, simply 246 | set the macro PTRACE_REFERENCE_FUNCTION to the name of a function in 247 | your library. You may #include any header files needed to make this 248 | function visible in the "User Macros" section of ptrace.c. 249 | 250 | How to modify it 251 | ---------------- 252 | If you do not like the name of the trace file (TRACE) you can actually 253 | rename that both in ptrace.c and etrace. You could also use a dynamic file 254 | name but you need then a way of exchanging the file name between both 255 | processes. 256 | 257 | If you prefer, you could have ptrace.c redirect its output to a socket and 258 | etrace retrieve it as a client. This would substantially increase the 259 | amount of code but could be interesting for remotely debugging an 260 | application. 261 | 262 | The output of your 'nm' command might slightly change from the BSD standard 263 | (identical to Linux, Solaris, and to some extent to the HPUX output). You 264 | will need to modify the simplistic parser implemented in 'etrace' to handle 265 | these changes. 266 | 267 | You could also use the tracing feature to have 'ptrace.c' send many more 268 | informations through the line, like memory allocations, file operations, 269 | etc. This might make the output somewhat harder to read and is most 270 | probably better handled by a debugger. 271 | 272 | Well anyway, this is just one possible use of the function instrumentation 273 | feature of gcc 2.95. Thanks to the gcc team for this VERY useful tool! 274 | 275 | 276 | 277 | Feedback 278 | -------- 279 | Feedback is always welcome. 280 | However, this code is not supported. This is only 80 lines of Python and 281 | 100 lines of C, so if you need additional features I'd say you are better 282 | off on your own. :-) 283 | 284 | 285 | N. Devillard 286 | Wed May 9 15:29:04 CEST 2001 287 | 288 | V. Chudnovsky 289 | Mon Mar 8 2004 290 | 291 | 292 | 293 | -------------------------------------------------------------------------------- /example/Makefile: -------------------------------------------------------------------------------- 1 | 2 | CC = gcc 3 | CFLAGS = -g -finstrument-functions --std=gnu99 4 | 5 | crumble: crumble.c ../src/ptrace.c 6 | $(CC) $(CFLAGS) -o crumble crumble.c ../src/ptrace.c 7 | 8 | run: 9 | touch TRACE ; 10 | crumble 11 | ../src/etrace crumble 12 | rm -f TRACE 13 | 14 | clean: 15 | rm -f crumble TRACE 16 | -------------------------------------------------------------------------------- /example/crumble.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | void Crumble_buy(char * what, int quantity, char * unit) 7 | { 8 | printf("buy %d %s of %s\n", quantity, unit, what); 9 | // sleep(1); 10 | } 11 | 12 | void Crumble_buy_stuff(void) 13 | { 14 | Crumble_buy("sugar", 125, "grams"); 15 | Crumble_buy("butter", 125, "grams"); 16 | Crumble_buy("wheat", 200, "grams"); 17 | Crumble_buy("salt", 1, "pinch"); 18 | Crumble_buy("apple", 5, "pieces"); 19 | // sleep(1); 20 | } 21 | 22 | void Crumble_skin_and_dice(void) 23 | { 24 | printf("skin apples and make little dices\n"); 25 | // sleep(1); 26 | } 27 | 28 | int Crumble_prepare_apples(void) 29 | { 30 | Crumble_skin_and_dice(); 31 | // sleep(1); 32 | return 1 ; 33 | } 34 | 35 | int Crumble_mix(char * a, char * b, char * c, char * d) 36 | { 37 | printf("mix %s with %s, %s and %s\n", a, b, c, d); 38 | // sleep(1); 39 | return 1; 40 | } 41 | 42 | 43 | int Crumble_put(char * what, char * where) 44 | { 45 | printf("put %s %s\n", what, where); 46 | // sleep(1); 47 | } 48 | 49 | void Crumble_finalize(int apples, int crumble) 50 | { 51 | Crumble_put("apples", "below"); 52 | Crumble_put("crumble", "on top"); 53 | // sleep(1); 54 | } 55 | 56 | void Crumble_bake(char * what, int temperature_deg, int time_min) 57 | { 58 | printf("cook %s at %d degrees for %d minutes\n", 59 | what, 60 | temperature_deg, 61 | time_min); 62 | // sleep(1); 63 | } 64 | 65 | void Crumble_cook(void) 66 | { 67 | Crumble_put("apple crumble", "in oven"); 68 | Crumble_bake("apple crumble", 220, 45); 69 | // sleep(1); 70 | } 71 | 72 | void Crumble_make_apple_crumble(void) 73 | { 74 | int apples ; 75 | int crumble ; 76 | 77 | Crumble_buy_stuff(); 78 | apples = Crumble_prepare_apples(); 79 | crumble = Crumble_mix("butter", "sugar", "wheat", "salt"); 80 | Crumble_finalize(apples, crumble); 81 | Crumble_cook(); 82 | // sleep(1); 83 | } 84 | 85 | int main(int argc, char * argv[]) 86 | { 87 | Crumble_make_apple_crumble(); 88 | return 0 ; 89 | } 90 | 91 | -------------------------------------------------------------------------------- /example2/Makefile: -------------------------------------------------------------------------------- 1 | 2 | CC = gcc 3 | CFLAGS = -g -finstrument-functions 4 | 5 | crumble: crumble.c ../src/ptrace.c 6 | $(CC) $(CFLAGS) -o crumble crumble.c ../src/ptrace.c 7 | 8 | run: 9 | touch TRACE ; 10 | crumble 11 | ../src/etrace.pl crumble 12 | rm -f TRACE 13 | 14 | clean: 15 | rm -f crumble TRACE 16 | -------------------------------------------------------------------------------- /example2/crumble.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | void Crumble_buy(char * what, int quantity, char * unit) 7 | { 8 | printf("buy %d %s of %s\n", quantity, unit, what); 9 | sleep(1); 10 | } 11 | 12 | void Crumble_buy_stuff(void) 13 | { 14 | Crumble_buy("sugar", 125, "grams"); 15 | Crumble_buy("butter", 125, "grams"); 16 | Crumble_buy("wheat", 200, "grams"); 17 | Crumble_buy("salt", 1, "pinch"); 18 | Crumble_buy("apple", 5, "pieces"); 19 | sleep(1); 20 | } 21 | 22 | void Crumble_skin_and_dice(void) 23 | { 24 | printf("skin apples and make little dices\n"); 25 | sleep(1); 26 | } 27 | 28 | int Crumble_prepare_apples(void) 29 | { 30 | Crumble_skin_and_dice(); 31 | sleep(1); 32 | return 1 ; 33 | } 34 | 35 | int Crumble_mix(char * a, char * b, char * c, char * d) 36 | { 37 | printf("mix %s with %s, %s and %s\n", a, b, c, d); 38 | sleep(1); 39 | return 1; 40 | } 41 | 42 | 43 | int Crumble_put(char * what, char * where) 44 | { 45 | printf("put %s %s\n", what, where); 46 | sleep(1); 47 | } 48 | 49 | void Crumble_finalize(int apples, int crumble) 50 | { 51 | Crumble_put("apples", "below"); 52 | Crumble_put("crumble", "on top"); 53 | sleep(1); 54 | } 55 | 56 | void Crumble_bake(char * what, int temperature_deg, int time_min) 57 | { 58 | printf("cook %s at %d degrees for %d minutes\n", 59 | what, 60 | temperature_deg, 61 | time_min); 62 | sleep(1); 63 | } 64 | 65 | void Crumble_cook(void) 66 | { 67 | Crumble_put("apple crumble", "in oven"); 68 | Crumble_bake("apple crumble", 220, 45); 69 | sleep(1); 70 | } 71 | 72 | void Crumble_make_apple_crumble(void) 73 | { 74 | int apples ; 75 | int crumble ; 76 | 77 | Crumble_buy_stuff(); 78 | apples = Crumble_prepare_apples(); 79 | crumble = Crumble_mix("butter", "sugar", "wheat", "salt"); 80 | Crumble_finalize(apples, crumble); 81 | Crumble_cook(); 82 | sleep(1); 83 | } 84 | 85 | int main(int argc, char * argv[]) 86 | { 87 | Crumble_make_apple_crumble(); 88 | return 0 ; 89 | } 90 | 91 | -------------------------------------------------------------------------------- /example2/crumble.h: -------------------------------------------------------------------------------- 1 | void Crumble_buy(char * what, int quantity, char * unit); 2 | 3 | -------------------------------------------------------------------------------- /src/etrace: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # $Revision: 2.1 $ 3 | 4 | import string, os, sys, stat, re 5 | 6 | # Create a TRACE file if none exists 7 | def init_trace(): 8 | try: 9 | os.stat("TRACE") 10 | except OSError: 11 | os.system("rm -f TRACE") 12 | os.mkfifo("TRACE") 13 | 14 | # Remove TRACE if it exists 15 | def end_trace(): 16 | if stat.S_ISFIFO(os.stat("TRACE")[stat.ST_MODE]): 17 | os.remove("TRACE") 18 | sys.exit(0) 19 | 20 | def hex_conv(s): 21 | return int(s, 16) 22 | 23 | # Load symbols 24 | def load_symbols(prog): 25 | pattern = re.compile("[^:]+:\s*([0-9a-fA-F]+)? (\w) (\w+)") 26 | f = os.popen("nm -o "+prog) 27 | sym = {} 28 | for line in f.readlines(): 29 | match = pattern.match(line) 30 | if not match or match.groups()[0] == None: 31 | continue 32 | addr, kind, name = match.groups() 33 | addr = hex_conv(addr) 34 | if name[0] == '_' and not name.startswith("__cyg_profile_func_"): 35 | name = name[1:] 36 | sym[addr]=name 37 | 38 | if "__cyg_profile_func_enter" not in sym.values(): 39 | print("Error: "+prog+" doesn't appear to be instrumented. ") 40 | end_trace() 41 | 42 | f.close() 43 | return sym 44 | 45 | 46 | # Print a program's trace 47 | def trace(prog): 48 | sym = load_symbols(prog) 49 | trace_file = os.path.abspath("TRACE") 50 | tr = open(trace_file, 'r') 51 | 52 | printer = Printer() 53 | 54 | while True: 55 | line = tr.readline() 56 | if line: 57 | s = line.split() 58 | 59 | if s[0]=="EXIT": 60 | end_trace() 61 | 62 | s1 = hex_conv(s[1]) 63 | 64 | if s1 in sym.keys(): 65 | name = sym[s1] 66 | else: 67 | name = "??" 68 | 69 | printer.printFunction(s[0], name) 70 | tr.close() 71 | 72 | class Printer: 73 | 74 | def __init__(self): 75 | self.level = 0 76 | self.prevname = "" 77 | self.count = 0 78 | 79 | def incr_count(self): 80 | self.count = self.count + 1 81 | 82 | def incr_level(self): 83 | self.level = self.level + 1 84 | 85 | def decr_level(self): 86 | self.level = self.level - 1 87 | 88 | def print_func(self, name): 89 | print ("\n " + self.level*" \t" + "\--" + name, end = '') 90 | 91 | def print_totals(self): 92 | print (" (total: %d times)" % (self.count+1), end = '') 93 | 94 | def do_func(self, name): 95 | if self.prevname == name: 96 | # was the counter counting? 97 | if self.count == 0: 98 | self.print_func(name) 99 | self.incr_count() 100 | else: 101 | # New name received. Was the counter counting? 102 | if self.count > 0: 103 | self.print_totals() 104 | self.count=0 105 | 106 | self.print_func(name) 107 | 108 | def printFunction(self, status, name): 109 | if status == "enter": 110 | self.do_func(name) 111 | # Check if new name compared to previous input 112 | self.incr_level() 113 | self.prevname = name 114 | else: 115 | self.decr_level() 116 | if name == "main": 117 | if self.count > 0: 118 | self.print_totals() 119 | print() 120 | end_trace() 121 | 122 | # Main 123 | if __name__=="__main__": 124 | if len(sys.argv)<2: 125 | print("Usage: analysis ") 126 | sys.exit(1) 127 | 128 | init_trace() 129 | trace(sys.argv[1]) 130 | -------------------------------------------------------------------------------- /src/etrace.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | # 3 | # etrace.pl script 4 | # Author: Victor Chudnovsky 5 | # Date: March 8, 2004 6 | 7 | use strict; 8 | use warnings "all"; 9 | 10 | our $NM = 'nm '; # unix nm utility to get symbol names and addresses 11 | 12 | # Output format 13 | our $TAB1 = "| "; # indentation: function still on the stack 14 | our $TAB2 = "\\-- "; # indentation: function just placed on the stack 15 | our $UNDEFINED_SYMBOL = "???"; # when an offset cannot be mapped to a symbol 16 | 17 | # Input format 18 | our $FIFO_NAME = "TRACE"; # default FIFO name 19 | our $REFERENCE_OFFSET = "REFERENCE:"; # marks a known symbol/object pair for dynamic libraries 20 | our $FUNCTION_ENTRY = "enter"; # marks entry into a function 21 | our $FUNCTION_EXIT = "exit"; # marks exit from function 22 | our $END_TRACE = "EXIT"; # end of input 23 | our $HEX_NUMBER = '0?x?[0-9a-fA-F]+'; # hex number 24 | our $SYMBOL_NAME = '[\w@.]+'; # valid identifier characters 25 | 26 | # Global identifiers 27 | our %SYMBOLTABLE; # : a hash array from relative offsets to symbol names 28 | our $IS_FIFO; # : are we reading from a FIFO or a file? 29 | #our CALL_DATA; # : file handle to input file/FIFO 30 | 31 | sub readObjects { 32 | my $objectFileName = shift; 33 | my $handleName; 34 | if (-x $objectFileName) { 35 | # Object code: extract symbol names via a pipe before parsing 36 | $handleName = $NM.$objectFileName.'|'; 37 | } else { 38 | # A symbol table: simply parse the symbol names 39 | $handleName = '<'.$objectFileName; 40 | }; 41 | open obj_file, $handleName or die "$0: cannot open $objectFileName"; 42 | my $line; 43 | while ($line = ) { 44 | $line =~ m/^($HEX_NUMBER)\s+.*\s+($SYMBOL_NAME)$/; 45 | my $hexLocation = $1; 46 | my $symbolName = $2; 47 | my $location = hex $hexLocation; 48 | $SYMBOLTABLE{$location} = $2 49 | } 50 | close obj_file; 51 | } 52 | 53 | sub writeSymbolTable { 54 | my $location, my $name; 55 | while ( ($location,$name) = each %SYMBOLTABLE) { 56 | print "[$location] => $name\n"; 57 | } 58 | }; 59 | 60 | sub establishInput { 61 | my $inputName = shift; 62 | if (!defined($inputName)) { 63 | $inputName = $FIFO_NAME; 64 | }; 65 | if (-p $inputName) { 66 | # delete a previously existing FIFO 67 | unlink $inputName; 68 | }; 69 | if (!-e $inputName) { 70 | # need to create a FIFO 71 | system('mknod', $inputName, 'p') && die "$0: could not create FIFO $inputName\n"; 72 | $IS_FIFO = 1; 73 | } else { 74 | # assume input comes from a file 75 | die "$0: file $inputName is not readable" if (!-r $inputName); 76 | $IS_FIFO = 0; 77 | } 78 | $inputName; # return value 79 | } 80 | 81 | sub deleteFifo { 82 | if ($IS_FIFO) { 83 | my $inputName = shift; 84 | unlink $inputName or die "$0: could not unlink $inputName\n"; 85 | }; 86 | }; 87 | 88 | 89 | sub processCallInfo { 90 | my $offsetLine = ; 91 | my $baseAddress; 92 | if ($offsetLine =~ m/^$REFERENCE_OFFSET\s+($SYMBOL_NAME)\s+($HEX_NUMBER)$/) { 93 | # This is a dynamic library; need to calculate the load offset 94 | my $offsetSymbol = $1; 95 | my $offsetAddress = hex $2; 96 | 97 | my %offsetTable = reverse %SYMBOLTABLE; 98 | $baseAddress = $offsetTable{$offsetSymbol} - $offsetAddress; 99 | $offsetLine = ; 100 | } else { 101 | # This is static 102 | $baseAddress = 0; 103 | } 104 | 105 | my $level = 0; 106 | while (!($offsetLine =~/^$END_TRACE/)) { 107 | if ($offsetLine =~ m/^($FUNCTION_ENTRY|$FUNCTION_EXIT)\s+($HEX_NUMBER)$/) { 108 | my $action = $1; 109 | my $offset = hex $2; 110 | my $address = $offset + $baseAddress; 111 | if ($1=~m/$FUNCTION_ENTRY/) { 112 | my $thisSymbol = $SYMBOLTABLE{$offset+$baseAddress}; 113 | if (! defined($thisSymbol)) { 114 | $thisSymbol = $UNDEFINED_SYMBOL; 115 | }; 116 | print $TAB1 x $level . $TAB2 . $thisSymbol . "\n"; 117 | $level++; 118 | } else { 119 | $level--; 120 | } 121 | } else { 122 | if ( (length $offsetLine) > 0 ) { 123 | chomp $offsetLine; 124 | print "Unrecognized line format: $offsetLine\n"; 125 | }; 126 | } 127 | $offsetLine = ; 128 | }; 129 | } 130 | 131 | 132 | 133 | # Main function 134 | if (@ARGV==0) { 135 | die "Usage: $0 OBJECT [TRACE]\n" . 136 | " OBJECT is either the object code or the output of $NM\n" . 137 | " TRACE is either the etrace output file or of a FIFO to connect to etrace.\n"; 138 | }; 139 | 140 | my $objectFile = shift @ARGV; 141 | my $inputFile = shift @ARGV; 142 | 143 | readObjects $objectFile; 144 | 145 | $inputFile = establishInput $inputFile; 146 | 147 | open CALL_DATA,"<$inputFile"; 148 | processCallInfo; 149 | close CALL_DATA; 150 | 151 | deleteFifo $inputFile; 152 | -------------------------------------------------------------------------------- /src/ptrace.c: -------------------------------------------------------------------------------- 1 | 2 | /*-------------------------------------------------------------------------*/ 3 | /** 4 | @file ptrace.c 5 | @author N. Devillard, V. Chudnovsky 6 | @date March 2004 7 | @version $Revision: 1.1.1.1 $ 8 | @brief Add tracing capability to any program compiled with gcc. 9 | 10 | This module is only compiled when using gcc and tracing has been 11 | activated. It allows the compiled program to output messages whenever 12 | a function is entered or exited. 13 | 14 | To activate this feature, your version of gcc must support 15 | the -finstrument-functions flag. 16 | 17 | When using ptrace on a dynamic library, you must set the 18 | PTRACE_REFERENCE_FUNCTION macro to be the name of a function in the 19 | library. The address of this function when loaded will be the first 20 | line output to the trace file and will permit the translation of the 21 | other entry and exit pointers to their symbolic names. You may set 22 | the macro PTRACE_INCLUDE with any #include directives needed for 23 | that function to be accesible to this source file. 24 | 25 | The printed messages yield function addresses, not human-readable 26 | names. To link both, you need to get a list of symbols from the 27 | program. There are many (unportable) ways of doing that, see the 28 | 'etrace' project on freshmeat for more information about how to dig 29 | the information. 30 | */ 31 | /*--------------------------------------------------------------------------*/ 32 | 33 | /* 34 | $Id: ptrace.c,v 1.1.1.1 2004-03-16 20:00:07 ndevilla Exp $ 35 | $Author: ndevilla $ 36 | $Date: 2004-03-16 20:00:07 $ 37 | $Revision: 1.1.1.1 $ 38 | */ 39 | 40 | #if (__GNUC__>2) || ((__GNUC__ == 2) && (__GNUC_MINOR__ > 95)) 41 | 42 | /*--------------------------------------------------------------------------- 43 | Includes 44 | ---------------------------------------------------------------------------*/ 45 | 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | 54 | /*--------------------------------------------------------------------------- 55 | User Macros 56 | ---------------------------------------------------------------------------*/ 57 | #define PTRACE_PIPENAME "TRACE" 58 | 59 | /* When using ptrace on a dynamic library, the following must be defined: 60 | 61 | #include "any files needed for PTRACE_REFERENCE_FUNCTION" 62 | #define PTRACE_REFERENCE_FUNCTION functionName 63 | 64 | `*/ 65 | 66 | 67 | /*--------------------------------------------------------------------------- 68 | Defines 69 | ---------------------------------------------------------------------------*/ 70 | 71 | #define REFERENCE_OFFSET "REFERENCE:" 72 | #define FUNCTION_ENTRY "enter" 73 | #define FUNCTION_EXIT "exit" 74 | #define END_TRACE "EXIT" 75 | #define __NON_INSTRUMENT_FUNCTION__ __attribute__((__no_instrument_function__)) 76 | #define PTRACE_OFF __NON_INSTRUMENT_FUNCTION__ 77 | #define STR(_x) #_x 78 | #define DEF(_x) _x 79 | #define GET(_x,_y) _x(_y) 80 | #define TRACE __GNU_PTRACE_FILE__ 81 | /*--------------------------------------------------------------------------- 82 | Function codes 83 | ---------------------------------------------------------------------------*/ 84 | 85 | /** Initial trace open */ 86 | static FILE *__GNU_PTRACE_FILE__; 87 | 88 | /** Code segment */ 89 | static void *segment; 90 | 91 | /** Final trace close */ 92 | static void 93 | __NON_INSTRUMENT_FUNCTION__ 94 | gnu_ptrace_close(void) 95 | { 96 | fprintf(TRACE, END_TRACE " %ld\n", (long)getpid()); 97 | 98 | if (TRACE != NULL) 99 | fclose(TRACE); 100 | return ; 101 | } 102 | 103 | /** Trace initialization */ 104 | static int 105 | __NON_INSTRUMENT_FUNCTION__ 106 | gnu_ptrace_init(void) 107 | { 108 | struct stat sta; 109 | __GNU_PTRACE_FILE__ = NULL; 110 | FILE *f; 111 | 112 | /* See if a trace file exists */ 113 | if (stat(PTRACE_PIPENAME, &sta) != 0) 114 | { 115 | /* No trace file: do not trace at all */ 116 | return 0; 117 | } 118 | else 119 | { 120 | /* trace file: open up trace file */ 121 | if ((TRACE = fopen(PTRACE_PIPENAME, "a")) == NULL) 122 | { 123 | char *msg = strerror(errno); 124 | perror(msg); 125 | printf("[gnu_ptrace error]\n"); 126 | return 0; 127 | } 128 | 129 | #ifdef PTRACE_REFERENCE_FUNCTION 130 | fprintf(TRACE,"%s %s %p\n", 131 | REFERENCE_OFFSET, 132 | GET(STR,PTRACE_REFERENCE_FUNCTION), 133 | (void *)GET(DEF,PTRACE_REFERENCE_FUNCTION)); 134 | #endif 135 | 136 | /* Tracing requested: a trace file was found */ 137 | atexit(gnu_ptrace_close); 138 | 139 | f = fopen("/proc/self/maps", "r"); 140 | fscanf(f, "%x", &segment); 141 | fclose(f); 142 | 143 | return 1; 144 | } 145 | } 146 | 147 | /** Function called by every function event */ 148 | void 149 | __NON_INSTRUMENT_FUNCTION__ 150 | gnu_ptrace(char * what, void * p) 151 | { 152 | static int first=1; 153 | static int active=1; 154 | 155 | if (active == 0) 156 | return; 157 | 158 | if (first) 159 | { 160 | active = gnu_ptrace_init(); 161 | first = 0; 162 | 163 | if (active == 0) 164 | return; 165 | } 166 | 167 | fprintf(TRACE, "%s %p\n", what, p - segment); 168 | fflush(TRACE); 169 | return; 170 | } 171 | 172 | /** According to gcc documentation: called upon function entry */ 173 | void 174 | __NON_INSTRUMENT_FUNCTION__ 175 | __cyg_profile_func_enter(void *this_fn, void *call_site) 176 | { 177 | gnu_ptrace(FUNCTION_ENTRY, this_fn); 178 | (void)call_site; 179 | } 180 | 181 | /** According to gcc documentation: called upon function exit */ 182 | void 183 | __NON_INSTRUMENT_FUNCTION__ 184 | __cyg_profile_func_exit(void *this_fn, void *call_site) 185 | { 186 | gnu_ptrace(FUNCTION_EXIT, this_fn); 187 | (void)call_site; 188 | } 189 | 190 | #endif 191 | /* vim: set ts=4 et sw=4 tw=75 */ 192 | --------------------------------------------------------------------------------