15-410 Project 0: Traceback
Table of Contents
Project OverviewIn this project you will be writing a "library" which contains a single function called trackback(). traceback() prints out a stack trace of the program it is called from. The stack trace will include all of the function calls made to reach the current location in the program. You will be provided with information about all of the functions available in the program and their arguments.
One example of a possible use for such a function would be to call it from a segmentation fault handler to help debug the program.
Traceback DetailsThe prototype for trackback, as defined in traceback.h, is
void traceback(FILE *);
The argument to traceback is the file stream to which the stack trace should be printed. For most programs, this will probably be stderr, but taking it as an argument allows for greater flexibility in the use of traceback.
Also defined in traceback.h is a table of all the functions in the program. Each function has the type functsym_t which contains the name of the function and the address at which the function begins along with a list of arguments. Each argument is defined as an argsym_t containing the type and name of the argument. The type is stored as an int and can be matched with the definitions in traceback.h. For the sake of simplicity, we are only requiring you to recognize char, int, float, double, char*, and char**.
If the function list contains fewer than MAX_NUM_FUNCTIONS it will be terminated by a function with a zero-length name. Similarly, if the argument list for a function contains fewer than MAX_NUM_ARGS arguments it will be terminated by an argument with zero length name. The functions in the list are sorted by address.
For each function you should print the name of the function and all of the arguments. When printing each argument you should output the name and the actual argument whenever the type is known. This means you must print the string in the case of a char* and all of the strings in the case of a char**. Be warned that you should not cause a segmentation fault while printing these values. You need not print anything other than the name for arguments for which the type is not known.
For those of you wondering how you can have a global variable containing all the function names in a library function, you normally can't within the C language framework. We have provided a script which fills in the empty table after a program is compiled and linked with the library (see the lecture notes for a diagram). This is not really the correct way to obtain this information; one should obtain it at runtime. The correct approach, however, is significantly more work than intended for this project and does not really add to the learning experience as it is just an exercise in jumping through hoops.
Formattingtraceback() should output the functions in order from the last (most recent) function called to the first function called. It should contain the name and values of all of the arguments (and void if there are no arguments). The output of traceback() should match the following sample partial output:
Function foo(i=5, f=35.000000f), in
Function foobar(c='k', str="test", unprintable=0xdeadbeef), in
Function bar(void), in
This indicates that some function (not shown) called bar() with no arguments. bar() then called foobar with a character 'k', a string "test", and an argument called unprintable, located at 0xdeadbeef in memory, which cannot be printed in a better way. foobar() in turn called foo with the arguments 5 and 35.
The following guidelines should help you to determine how arguments should be displayed:
GoalsDespite the fact that this is the smallest project of the five that will be assigned in this class, it is important to pay attention to the key concepts in Project 0. The ideas taught here will provide the foundation for the next three projects. In particular, we would like you to be comfortable with:
Getting StartedYou will probably find yourself wishing for some information which is not easily available within the C language framework, so you will need to write some x86 assembly language. You may do this by writing a C-callable function in a .S file (note that the 'S' is upper-case) or by using the asm() in-line assembly language facility. Either one will work, but in practice it is very difficult to write a run of in-line assembly language longer than three to five instructions and have the result be easily readable. The support code includes a sample .S file, and you can find asm() covered in the gcc documentation.
Important DatesWednesday, January 14th: Project 0 assigned.
Wednesday, January 21st: Project 0 is due at 11:59pm.
TestingIt is important that your trackback() function be able to deal with any sort of program in which someone might wish to use it. You must ensure that it will work properly regardless of where it is called within any program. Take some time to develop the evilest test cases that you can because while grading we will submit your code to the most diabolical tests we can imagine. Of course if your code is well written, it should have no problems passing these tests.
DocumentingCommenting is an important part of writing code. If you wish, you may get a jump on things by using doxygen; see our doxygen documentation to see how to include comments in your code that can be read by doxygen. When we grade your projects, we will begin with your documentation. Lack of documentation will be reflected in your grade. The provided traceback.h file contains example doxygen comments with the sort of information we are expecting to see. Although we put the doxygen comments for our functions in the .h file, you should typically put yours .c file, with each function's comment block adjacent to the code. In addition, we have provided a rule in the Makefile to take care of generating the documents for you. This rule is make html_doc and if you have set this up to work we will run it as part of grading.
Other Important Notes
Hand-in InstructionsYou will be required to hand in all your .c, .s, .h, and any other files necessary to run your code. Minimally this will include the traceback function and any support functions that it requires. When we run your code, it should display the behavior described in the Traceback Details section above.
We have had several questions about how you can determine whether a given address is valid (i.e., backed by memory) during the execution of a process. Like many other questions which will arise as this course unfolds, there are multiple approaches, with different tradeoffs. In general you should strive to identify two to three approaches and choose among them based on weighing a variety of criteria.
But for Project 0, since it is a warm-up, it seems appropriate to give a few hints.
[Last modified Wednesday January 21, 2004]