![]() |
Chapter 3 | ![]() |
These two tools (I'll just refer to them both collectively as 'grep' from now on) are of incredible value, as they let you find your problem in just a few quick jumps, even if you don't know the code that you're looking for. We'll use a real world example for this, but keep in mind that I'll be using the grep-specific commands, as the windows version simply consist of typing in a box and hitting 'find'.
While I could generate my own multiple-file project and include it with this guide, it appears I don't have to, as many others have already been generated. I like MUDs, so we're going to use the CircleMUD distribution which is sufficiently complex for our needs. It is located at ftp://ftp.circlemud.org/pub/CircleMUD/3.x/circle30bpl20.zip.
Uncompress this program, and place it in a directory. If you wish you may compile the source code, though for our exercise, it won't be necessary. Specific instructions are given for the compiler you're using, in the /doc directory.
free(ch);
This should be directly before the line reading "GET_INVIS_LEV(ch) = 0;" If it's not, adjust the location of the statment you added approprately.
You've just inserted a crash bug into the code. Now the hard part, forget everything you just did, and we'll start the example.
Unfortunately for us, we have no idea what the code is like - this is our first day working with it!
Still, it should be pretty easy. We know the command that was typed to crash the program, so we should start looking through the code for that. I use grep to search for the word 'visible' in my .c and .h files, making sure to ignore the difference between upper and lowercase with the 'i' switch, and printing the line numbers for matches with -n:
> grep -in visible *[ch] |
If you perform this command yourself, you'll notice you end up with one or two pages of matches - the word VISIBLE shows up alot in the code. How do you know which one is the one you're really after? Well, time to use your C programming knowledge. Inorder to perform a specific command, the program has to evaluate what a user types - in this case, the word 'visible'. There's probably a list or table of command words somewhere, and one of them matches the word 'visible'. What we're looking for then, is the word "visible" in double quotes - it's being assigned to a list, or char *, so it can be compared to user input. Sure enough, hiding in the latter part of the list is the line
interpreter.c:510: { "visible" , POS_RESTING , do_visible , 1, 0 },
Well, it's interesting, and gives us some sort of pointer to do_visible, but it's time to find out how it works, and what functions it runs. For that to happen, we'll need to pop open interpreter.c. Upon opening the file to line 510, we can see that this is one line in an array called cmd_info which is an array of type struct command_info. Apparently this structure holds all the commands available to users as well.
We've found ...something... what though, we may never know. The impatient among you may want to search for the 'do_visible' string - and you'd be right to do so. It's the function that's called in this array. The cautious of you though, may want to make certain that you're not jumping too far ahead without proof, so let's prove that this is the correct path.
We'll search for all instances of the array cmd_info in all the .c files, and there will be hits in several files - one of which is interpreter.c. We know that interpreter.c houses the command list, it's logical to assume that it also contains the code to parse through it. This idea is reinforced by this not-so-innocent looking line:
interpreter.c:638: ((*cmd_info[cmd].command_pointer) (ch, line, cmd, cmd_info[cmd].subcmd));
In case you can't tell immediately, this is a function call. It's a complex way to do it that isn't regularly used, but it's completely valid. Even if you were not completely familiar with C code, you can readily establish that it's not calling any flow operators (while, for, etc), nor is it an assignment or evaulation, and about the only thing left for a valid line of C code is to declare a variable or function, or call a function. There's obviously no declarations here, so ... it must be calling a function.
Anyway, the function it's calling is located in the 'command_pointer' member of the command_info struct, in our cmd_info array. Cross checking the cmd_info structure definition (in interpreter.h), we see that the cmd_info holds the command_pointer in the third member of the array, thus, "do_visible" is a function that is being called!
In the future, you'll find alot of situations like this; an array with the relevant data all closely packed together. It's good to find out how they work once, but don't feel like you have to do it every time. If another command causes a crash, you can just skip right to the 3'd array member! If you get familiar enough with CircleMUD or other code, you'll recognize these things automatically, and can skip even further ahead. Still, it all depends on looking it up that first time.
Lets continue, since you want to know where do_visible is. Another grep:
> grep -n do_visible *[ch]] |
Aside from the bits in interpreter.c that we've already seen, it appears twice in act.other.c, and one is a prototype, so it's fairly obvious:
act.other.c:347:ACMD(do_visible)
The nitpickers of you may want to find out what that ACMD thing is - but I'll shortcut you here..it's a macro that defines a specific function prototype. Where did I see that? Well, it's in interpreter.h, and I just happened to notice it when I was going through it. Of course, I could have just grepped for it.
Opening up the file at this point shows us the whole function, and the first and second lines:
if (GET_LEVEL(ch) >= LVL_IMMORT) { perform_immort_vis(ch);
Describe pretty succinctly what happens if an immortal types this command. It calls the perform_immort_vis() function. There's no apparent error here, so time to grep for that.
> grep -n perform_immort_vis *[ch] |
We find that the function is defined in act.wizard.c - the other places, it's simply being prototyped or called. It's a moderately popular function.
act.wizard.c:1410:void perform_immort_vis(struct char_data *ch)
If we open up the act.wizard.c file at this point and simply read through that function, you'll notice that a pointer is freed, and then immedately operated on - this is probably (is in acutality) our crash bug, on line 1416!
Remember though, grep isn't just a magic gun that nails the problem in one shot. It's just a tool that you have to have intelligence to use. Try the above example starting with 'grep -in invis *[ch]'. There's about 4-5 pages of matches easily; a bit too much to run through quickly. Learning to use regexs will help, but knowing the code is a much better solution - whether you limit it by files, or knowing exact text strings to search for. For example, I could have looked for the string "You are now fully visible" which an immortal should get when they type 'visible'. If they don't get it, I know the problem is 'above' it, in the trace, and if they do, I know it's right after it. Best of all, it only takes one search to find the 'perform_immort_vis()' function.
![]() |
Index | ![]() |
4.2 Unix uses Grep | 5. Debugging Through Logging |