The following threads are tangentially related:
For the first part of my thesis, I spent 18 months modifying undocumented Fortran code, and one of the first tasks was to try and understand the overall structure of a code base. The most important thing I suggest you do is take notes in a text file any time you figure something out. You don't want to have to relearn or rediscover things during this time-consuming and frustrating process.
In my case, there was no "API" to speak of, in that the arguments of functions weren't self-documenting, because the previous programmer used Fortran 77-like style, and thus, short identifiers with little to no hint of what they meant. There were no tests, and since it's Fortran, no headers. Adding even more fun to the mix, there were a couple functions here and there written in C or C++.
Things that worked for me (assuming you work in Linux):
grep
. Learn to love grep
; you'll use it in the shell to find where functions are declared and called, and the output will tell you what files to look in.
- The "find" command in your favorite code editor or IDE. Once you know what file to look in, the "find" command will save you time in hunting around for function calls.
- Aggressive refactoring, if you can change the code base. I made the variable names self-documenting so that I didn't have to spend mental effort relearning things I'd already figured out. I also streamlined the design of the code as I figured it out so it was less confusing. No more 1000 line long main program!
- Aggressive commenting. Again, if you can change the code base, comment things so that you know what you figured out. I didn't use Doxygen, but Doxygen is good for this.
nm
. It can be useful on libraries if you don't have source code for them, but want to know if a function you encountered is in that library. However, it only works if the library's symbols haven't been stripped.
- Step through the code with a debugger. It's way more efficient than using
print
statements. ddd
and gdb
are great, and on virtually every Linux system out there. Feel free to use your favorite debugger.
- Bug the developers. This option is really best for very targeted questions. If you go to them (as I did), and say, "I don't understand what's going on here," they might take pity on you and try to explain things in general detail, but that's going to be of limited use in the long run. You're going to have to do the leg work, because the developers didn't do it for you in writing documentation and explaining the structure of their code base in writing. The developers are really good for when you're really stuck on small things (if they remember what they did).
Things I'd wish I'd thought of earlier, or just weren't options for me:
- Doxygen. If you tweak the Doxyfile options, Doxygen will automatically generate a lot of documentation for you, even without the special Doxygen commenting syntax, which could be a good place to start; I've used this on later projects I encountered and it's been incredibly helpful.
- Unit testing. If you can alter the code base, and you have a some idea of what it's supposed to do, write unit tests for various functions. (It's a useful skill to pick up regardless.)
- If you're working with C/C++, look at the headers.
- Write example programs. Not an option for me on that Fortran project, but it's been useful for me in picking up third-party APIs. Also, look at their example programs, if they have any.
- Use
gcov
and lcov
to do a coverage analysis on typical runs of the code, if you have examples or executables to work with. If there are examples that are supposed to execute large parts of the code base, these two tools combined will tell you how many times each line of code is visited. It's most useful with debugging flags enabled. If a part of the code isn't visited at all, it's probably less important to understand it right away. If a part of the code is visited a lot, it's probably worth understanding what it is. Maybe the code is executed a lot because it's an unimportant loop, or it could be a key function that a lot of other functions rely on. You can't tell just on the coverage analysis, but the coverage analysis gives you some idea of where to focus your time.
- Static code analysis tools like
splint
can tell you if something fishy is going on in the code, like some variables are never used.
- Profiling. Again, gives you data that doesn't immediately tell you what is or is not important, but suggests what might be important. If a lot of CPU time is spent calling one function, you may want to look at that and see what it does. You can also use the profiling output with
dot
and graphviz
to generate call graphs and see how many times functions are called, like the coverage analysis. For complex codes, a graphical analysis could be a lot more helpful.
- If you're working in C, Frama-C is supposed to be helpful in analyzing code, but I've never used it because it seemed too complicated to be worth the effort. I do some work in pure C, but it's mostly code that I write; I've never worked with undocumented C code.