============================== Dr. Frank ===================================== Dr. Frank (DRFRANK.EXE) is a program that will help you perform a post-mortem on your Unrecoverable Application Errors (UAE's) with Microsoft Windows. It is part of a suite of tools (described below) to assist programmers, and was written with the programmer in mind, rather then the non-programmer. Dr. Frank is similar to DRWATSON, and uses the same TOOLHELP.DLL, which is currently in beta testing, and available on Compuserve from the MSOPSYS forum. DRWATSON and TOOLHELP.DLL are Microsoft products. When an exception occurs, Dr. Frank will intercept it, and write a file to your disk with information helpful to finding the cause of the exception. Since Dr. Frank is oriented towards developers, it writes out a short, concise report. It doesn't write out information that a developer might not always want, such as the system configuration, etc... If there is a strong desire for something like that, I may put in an option to write such information to the log file. If there are .SYM files present in the directory where a particular .EXE or .DLL is located, Dr. Frank will use it to provide a symbolic reference in the stack walk. See the TMAPSYM and EXEMAP sections below for more information on .SYM files. If you do not have .SYM files available, but do have Turbo Debugger symbolic information available, then you can use DFA.EXE (Dr. Frank Assistant) to create a symbolic stack trace from the DRFRANK.LOG file. See the DFA section below for more information on DFA. == Using Dr. Frank To use Dr. Frank, you will need to have TOOLHELP.DLL in your path. The easiest way to use Dr. Frank is to put it in the "load=" section of your WIN.INI. Upon starting up, Dr. Frank will minimize itself. It will remain minimized until an exception occurs, or you double click on it's icon. Double clicking on the icon will pull up a dialog box that tells you that Dr. Frank is "at work". If you wish to stop Dr. Frank, select "close" from its system menu. Options for Dr. Frank are specified in the WIN.INI file as follows: LogDir=[directory to put DRFRANK.LOG file]. This defaults to the Windows directory if none is specified. NewLog=0/1 Specifying 0 will cause Dr. Frank to always append reports to the previous DRFRANK.LOG file. Specifying 1 will cause Dr. Frank to overwrite the previous .LOG file the first time an exception occurs. Subsequent exceptions that occur during the same Windows session will be appended to the .LOG file ShowFileNames=0/1 Specifying 1 will cause Dr. Frank to display the file name of the module after each entry in the stack trace portion. This must be set for DFA.EXE (Dr. Franks Assistant) to work. If 0 is specified, the filename will not be output. ShowFileNames=1 is the default. StackData=0/1 Specifying 1 will cause Dr. Frank to add a verbose stack trace display to the end of the DRFRANK.LOG file. Each entry displays the memory at a positive offset from the SS:BP for that stack frame. If there are > 256 bytes between 2 successive stack frames, the memory display is omitted for that frame. Specifying 0 for the StackData will cause the verbose stack trace to not be generated. This is the default. AuxOut=0/1 Specifying 1 will cause Dr. Frank to write an abbreviated form of it's report to STDAUX when writing the .LOG file. To use this, you should have either be using a device driver that redirects STDAUX to a second monitor, or a terminal connected to STDAUX. Here is an example of my entry in the WIN.INI file: [DrFrank] logdir=C:\DRFRANK NewLog=1 ShowFileNames=0 StackData=0 AuxOut=1 == Interpreting the DRFRANK.LOG file To understand a DRFRANK.LOG file, it is important to understand the difference between a "logical" segment, and a selector. Windows .EXE files are called "New EXE" files, because they have a different format then traditional MS-DOS files. When New EXE files are linked, each segment is placed in a different section of the file. At the same time, a segment table is created, which allows Windows and other programs to quickly find the data for a particular segment. When referring to a particular segment, its position in the segment table is used. Thus, the first segment in the table is logical segment 1, the second is logical segment 2, etc... You can determine the number of segments, their size, and other information by running TDUMP (A Turbo Debugger utility) on the file. When the program is loaded, Windows assigns each logical segment it's own selector. Selectors are what is put into the CPUs segment registers. Note: For any given .EXE or .DLL, the logical segment that a procedure or function is in will never change. The selector value on the other hand, depends upon what selector Windows decided to allocate for the particular logical segment. In other words, the selector Windows uses for a particualar logical segment can change between different invocations of the .EXE or .DLL. A logical address is comprised of a module name, a logical segment, and an offset. A physical address is comprised of a selector and an offset. There is a 1 to 1 mapping between a given logical address, and its corresponding physical address. In the DRFRANK.LOG file, addresses are given in terms of the the logical addresses as well as physical address The logical address appears first, followed by the physical address in parenthesis. == Contents of the DRFRANK.LOG file The first line contains the date and time that the exception occurred. The next line tells you which exception occured, and where. The Disassembly section gives you physical addresses and disassembly for the faulting instruction, and several instructions after it. The stack trace section tells you what was happening when the exception occured. A typical entry: Stack frame number | Module name | | Logical address | | | Physical address | | | | Function name | | | | | offset within function | | | | | | 0 KERNEL 0001:5E2B (00FD:5E2B) LSTRLEN + 000D The fuction name will only be present if there is a .SYM file for the given module. The .SYM files should be in the same directory as the module. For instance, if KRNL286.EXE is in C:\WINDOWS\SYSTEM, then make sure that KRNL286.SYM is in that same directory. If you don't have .SYM files for a particular module, try using EXEMAP/TMAPSYM, described below. Note: The function name given must be taken with a grain of salt. Dr. Frank will look for the closest symbol name that appears before the frame address in the .SYM file. Some .SYM files do not contain information for undocumented functions. Thus, the function name appearing in the DRFRANK.LOG file will that of the closest documented function thats address precedes the frame address. If the offset field appears too high, be suspicious. If you didn't have .SYM files to assist with function names, but you do have TD debug information, then DFA can use the stack trace to produce a second stack trace with function names filled in. The registers section tells you the values in the registers at the time of the exception. The optional Verbose Stack Trace section is for people who wish to see what was on the stack at the time of the exception. For each stack frame that doesn't exceed 256 bytes a hex dump is performed, starting at SS:BP. This data can be used to get the values of parameters that were passed to the function. For instance: DialogBox() takes 4 parameters, and is a FAR PASCAL function. On the stack, you will have: [BP+0] ; Previous BP, incremented by 1 [BP+2] ; Far return address [BP+6] ; hInstance [BP+8] ; lpTemplateName [BP+0C] ; hWndParent [BP+0E] ; lpDialogFunction ============================= TMAPSYM ===================================== TMAPSYM.EXE was written to create .SYM files from Borland style .MAP files. Other .MAP to .SYM file converters may not work properly with Borland .MAP files. The main problem is with the fully parameterized names put out by C++ programs. TMAPSYM has one command line option, namely the name of the .MAP file to be processed. For instance: TMAPSYM mytest.map This will create a file called MYTEST.SYM in the current directory. It is generally faster to have TLINK generate a link map, and use TMAPSYM, then it is to use DFA. Also, by using TMAPSYM, you get an immediate symbolic stack trace, as opposed to having to manually run DFA after an exception occurs. Note: If you are using precompiled headers with BC++, the default extension of the precompiled headers file is .SYM. If you use TMAPSYM, you might inadvertantly overwrite the .SYM file from the compiler. Since other utilities, as well as the Windows debugging version will only look for .SYM files, I would recommend using the -H=filename option with BC++ to name the precompiled headers file to a non-conflicting name. ============================= EXEMAP ===================================== EXEMAP was written to create .MAP files from New EXE file format files (.EXE, .DLL, .DRV, etc...) for which you do not have source code or a .MAP file for. .MAP files created from EXEMAP can be processed by TMAPSYM to create .SYM files. These .SYM files are then in turn used by Dr. Frank One particularly good use of EXEMAP is to create .MAP files for DLL's when a .SYM file is not provided. Another reason why EXEMAP was written is because many .SYM files are incomplete. Oftentimes undocumented functions do not appear in the .SYM files. Since EXEMAP works directly from the executable file, many additional function entry points can be found, and included in your .SYM files. The syntax for EXEMAP is: EXEMAP [output file name] The default output filename, if none is supplied, is the input filename, with a .MAP extension. One caveat when using TMAPSYM is that it will only be able to find functions that are in the entry table of the executable. Thus, public functions that are not exported will not be found. In general this is not a problem. =============================== DFA ====================================== DFA.EXE (Dr. Frank Assistant) was written to allow symbolic stack traces to be obtained via the DRFRANK.LOG file, in conjunction with Turbo Debugger symbol tables. DFA will give you a file that contains a stack trace similar to the one in the DRFRANK.LOG file, but with function names and line numbers from the TD file. The syntax for DFA is: DFA [output filename] If no output filename is specified, DFA will use "DFA.OUT" as the default output filename. In order for DFA to work, you must have: ShowFileNames=1 in the [DrFrank] section of the WIN.INI file. By setting this option, you cause Dr. Frank to output the name of the .EXE or .DLL file for each module in the stack trace section of the DRFRANK.LOG file. DFA.EXE will use these filenames to attempt to find Turbo Debugger information to aid in creating the symbolic stack trace in the output file. First, the filename that is specified in the DRFRANK.LOG file is inspected for TD debug information. If none is found, the file extension is changed to .TDS. If there is no file by that name, then the stack trace entry symbolic name field is left blank. ================= Support for Dr. Frank and Tools ========================= Dr. Frank is Freeware for the time being. All I ask is that you let me know what you think, and if you have suggestions, or think you have a bug, to let me know. I will try to answer questions, time permitting, but I cannot always guarantee a reply. My Compuserve ID is: 76117,1720 Also, my main hangouts on Compuserve are BPROGB, section 3 (Debugger/ Profiler), and MSOPSYS, section 9. Matt Pietrek ===== Legal Disclaimer === Windows, MS-DOS, DRWATSON and TOOLHELP.DLL are trademarks of Microsoft Inc. Turbo Debugger, TLINK and TDUMP are trademarks of Borland International. All other names are trademarks of their respective holders. I make no claims as to the usability of these programs, and am not liable for any damages, incidental or consequential arising out of the use of these programs. IANAL, so I hope I got this right...