home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / sherlock.zip / DEBUG.C < prev    next >
C/C++ Source or Header  |  1994-10-07  |  16KB  |  679 lines

  1. /*
  2. **  Sherlock - Copyright 1992, 1993, 1994
  3. **    Harfmann Software
  4. **    Compuserve: 73147,213
  5. **    All rights reserved
  6. */
  7. /*
  8. **  Beginning of a debugger.
  9. */
  10. #include    <signal.h>
  11. #include    <stdio.h>
  12. #include    <stdlib.h>
  13. #include    <setjmp.h>
  14. #include    <ctype.h>
  15. #include    <string.h>
  16. #include    <sys\stat.h>
  17. #define     INCL_DOSSESMGR
  18. #define     INCL_DOSPROCESS
  19. #define     INCL_DOSEXCEPTIONS
  20. #include    <os2.h>
  21. #include    "debug.h"
  22.  
  23. #include    "Debugger.h"
  24. #include    "BrkPoint.h"
  25. #include    "Watch.h"
  26. #include    "Register.h"
  27. #include    "Except.h"
  28. #include    "SrcInter.h"
  29. #include    "Source.h"
  30. #include    "SrcDisp.h"
  31. #ifndef SHERLOCK
  32. #include    "ProcStat.h"
  33. #endif
  34.  
  35. #define MAX_ARGS 10
  36.  
  37. struct ThreadList {
  38.     struct ThreadList  *next;
  39.     TID         tid;
  40. } threadList = { NULL, 1};
  41.  
  42. static char *ptrs[MAX_ARGS];        /* Pointers to command arguments.    */
  43. #define MAX_COMMAND 256
  44. static char buff[MAX_COMMAND];        /* Command buffer.            */
  45. static char dupBuff[MAX_COMMAND];   /* Command buffer copy.        */
  46. static jmp_buf    JumpBuff;        /* Break jump buffer.        */
  47. static char logFileName[260] = "SHERLOCK.LOG";    /* Default log file.    */
  48.  
  49. /*
  50. ** Function prototypes for the files static functions.
  51. */
  52. static void APIENTRY Cleanup(void);
  53. static void GetCommand(char *buff, int sizeBuff, char **ptrs, int sizePtrs);
  54. static int DoCommand(char **ptrs);
  55.  
  56. /*
  57. ** Terminate the debugger.
  58. */
  59. static void APIENTRY Cleanup()
  60. {
  61.     FreeAllBreakpoints();
  62.     FreeAllWatchpoints();
  63.     FreeAllModules();
  64.     DosExitList(EXLST_EXIT, (PFNEXITLIST) Cleanup);
  65.     return;
  66. }
  67.  
  68. /*
  69. ** Function to dump the exception data.
  70. */
  71. static void dumpExceptionData(void);
  72. static void dumpExceptionData()
  73. {
  74. ULONG curTID;
  75. struct ThreadList *link;
  76.  
  77.     curTID = debugBuffer.Tid;
  78.     fprintf(logFile, "Exception in thread: %d\n\n", curTID);
  79.     for(link=&threadList; link; link=link->next) {
  80.         debugBuffer.Tid = link->tid;
  81.         if(DispatchCommand(DBG_C_ReadReg) != DBG_N_Success)
  82.             continue;
  83.         DumpRegs(&debugBuffer);
  84.         DumpStack(link->tid);
  85.     }
  86.     debugBuffer.Tid = curTID;
  87. }
  88.  
  89. /*
  90. ** If we get a ^Break or ^C:
  91. **   1) then stop program execution
  92. **   2) Prompt for kill debuggee
  93. **   3) Either continue or stop
  94. **
  95. */
  96. static void breakHandler(int event);
  97. static void breakHandler(int event)
  98. {
  99. int    stop;
  100.  
  101.     /*
  102.     ** Reset the break handler.
  103.     */
  104.     signal(event, breakHandler);
  105.  
  106.     /*
  107.     ** Stop the debuggee for now.
  108.     */
  109.     debugBuffer.Tid = 0;
  110.     DispatchCommand(DBG_C_Stop);
  111.  
  112.     /*
  113.     ** Ask whether to stop the debuggee if in the debugger.
  114.     ** If in Sherlock, stop the debuggee.
  115.     */
  116. #ifdef SHERLOCK
  117.     stop = 1;
  118. #else
  119.     fprintf(stderr, "Stop debug process? (Y/n)");
  120.     fflush(stderr);
  121.     while(1) {
  122.     char    ans;
  123.  
  124.     ans = getchar();
  125.     if((ans == 'Y') || (ans == 'y') || (ans == '\n') || (ans == '\r')) {
  126.         stop = 1;
  127.         break;
  128.     }
  129.  
  130.     if((ans = 'N') || (ans == 'n')) {
  131.         stop = 0;
  132.         break;
  133.     }
  134.     }
  135. #endif
  136.  
  137.     /*
  138.     ** If we wish to stop, jump to debug loop and continue.
  139.     */
  140.     if(stop)
  141.     longjmp(JumpBuff, 0);
  142.  
  143.     /*
  144.     ** If response is not to stop, then continue.
  145.     */
  146.     DispatchCommand(DBG_C_Go);
  147.     return;
  148. }
  149.  
  150. /*
  151. ** Get a command from the user and parse it into pieces.
  152. */
  153. static void GetCommand(char *buff, int sizeBuff, char **ptrs, int sizePtrs)
  154. {
  155. char *p;
  156. int  i;
  157.  
  158.     fflush(logFile);
  159.     for(i=0; i<sizePtrs; i++)
  160.     ptrs[i]=NULL;
  161.  
  162. restart:
  163.     fprintf(logFile, "\nCommand>");
  164.     fflush(logFile);
  165.     fgets(buff, sizeBuff, stdin);
  166.  
  167.     p = buff;
  168.     ptrs[0] = dupBuff;
  169.     for(i=0; i<(int)strlen(buff); i++) {
  170.     if(isspace(buff[i]))
  171.         buff[i] = ' ';
  172.     }
  173.     for(i=strlen(buff)-1; i>=0 && isspace(buff[i]); i--) {
  174.     if(isspace(buff[i]))
  175.         buff[i] = 0;
  176.     }
  177.  
  178.     /*
  179.     ** Set up the full argument list parameter as index 0
  180.     */
  181.     while( isspace(*p) && *p) p++;
  182.     while(!isspace(*p) && *p) p++;
  183.     while( isspace(*p) && *p) p++;
  184.     if(*p) {
  185.     strcpy(dupBuff, p);
  186.     ptrs[0] = dupBuff;
  187.     } else {
  188.     ptrs[0] = NULL;
  189.     }
  190.  
  191.     /*
  192.     ** For convienience, break the space separated parameters.
  193.     */
  194.     p = buff;
  195.     for(i=1; i<sizePtrs; i++) {
  196.     if(*p == 0)
  197.         break;
  198.  
  199.     /*
  200.         ** Strip off the leading white space.
  201.     */
  202.         for(;isspace(*p) && *p; p++)
  203.             ;
  204.  
  205.         /*
  206.         ** Skip over parameter?
  207.         */
  208.         if(*p == ',') {
  209.             continue;
  210.         }
  211.  
  212.         /*
  213.     ** No more on line?
  214.     */
  215.     if(*p == 0)
  216.         break;
  217.         ptrs[i] = p;
  218.  
  219.         /*
  220.         ** Quote delimited strings are allowed.
  221.         */
  222.         if(*p == '\'') {
  223.             p++;
  224.             ptrs[i] = p;
  225.             for(; *p && *p != '\''; p++)
  226.                 ;
  227.             if(*p != '\'') {
  228.         fprintf(logFile, "ILLEGAL STRING!\n");
  229.                 goto restart;
  230.             }
  231.  
  232.         /*
  233.         ** Double quote string.
  234.         */
  235.         } else if(*p == '"') {
  236.             p++;
  237.             ptrs[i] = p;
  238.             for(; *p && *p != '"'; p++) ;
  239.             if(*p != '"') {
  240.         fprintf(logFile, "ILLEGAL STRING!\n");
  241.                 goto restart;
  242.             }
  243.  
  244.         /*
  245.         ** Normal parameter.
  246.         */
  247.         } else {
  248.             for(;!((*p == ',') || isspace(*p)) && *p; p++)
  249.                 ;
  250.         }
  251.  
  252.     /*
  253.     ** If we are at the end of the line, drop it.
  254.     */
  255.     if(*p == 0)
  256.         break;
  257.  
  258.         /*
  259.     ** Null terminate the parameter and then continue;
  260.     */
  261.         *p = '\0';
  262.     p++;
  263.  
  264.         /*
  265.         ** Drop a trailing , if it exists.
  266.         */
  267.         if(*p == ',')
  268.             p++;
  269.     }
  270.  
  271.     /*
  272.     ** If we don't have any commands, try again!
  273.     */
  274.     if(ptrs[1] == NULL)
  275.         goto restart;
  276.  
  277.     return;
  278. }
  279.  
  280. /*
  281. ** Dispatch a command.
  282. */
  283. static int DoCommand(char **ptrs)
  284. {
  285. char    cmd;
  286.  
  287.     cmd = (char) tolower(ptrs[1][0]);
  288.  
  289.     /*
  290.     ** Quit?
  291.     */
  292.     if(cmd == 'q')
  293.     return 666;
  294.  
  295.     /*
  296.     ** Redisplay where we currently are.
  297.     */
  298.     if(cmd == '.') {
  299.     DebugModule *module;
  300.     char        funcName[MAX_FUNCNAME];
  301.     char        sourceName[CCHMAXPATH];
  302.     ULONG        lineNum;
  303.  
  304.     debugBuffer.Addr = Linearize(debugBuffer.EIP, debugBuffer.CS);
  305.     DispatchCommand(DBG_C_AddrToObject);
  306.     if((debugBuffer.Cmd == DBG_N_Success) && (debugBuffer.Value & 0x10000000)) {
  307.         module = FindModule(debugBuffer.MTE, NULL);
  308.         FindSource(module, Linearize(debugBuffer.EIP, debugBuffer.CS),
  309.                funcName, sourceName, &lineNum);
  310.         DisplaySource(module, sourceName, lineNum);
  311.     }
  312.     return -1;
  313.     }
  314.  
  315.     /*
  316.     ** Stack dump.
  317.     */
  318.     if(cmd == 'k') {
  319.     int threadID;
  320.     char *dummy;
  321.  
  322.     threadID = debugBuffer.Tid;
  323.     if(ptrs[2] != NULL)
  324.         threadID = strtoul(ptrs[2], &dummy, 0);
  325.     DumpStack(threadID);
  326.         return -1;
  327.     }
  328.  
  329.     /*
  330.     ** Talk to the watchpoint module to do the watchpoints.
  331.     */
  332.     if(cmd == 'w') {
  333.     WatchCommand(ptrs);
  334.     return -1;
  335.     }
  336.  
  337.     /*
  338.     ** Display an expression.
  339.     */
  340.     if(cmd == '?') {
  341.         ViewVariableCommand(ptrs);
  342.         return -1;
  343.     }
  344.  
  345.     /*
  346.     ** Display an expression.
  347.     */
  348.     if(cmd == 'd') {
  349.     ULONG    startAddr;
  350.     ULONG    endAddr;
  351.     char    buff[80];
  352.     UCHAR    data[16];
  353.     char   *dummy;
  354.  
  355.     if(ptrs[2] != NULL)
  356.         startAddr = strtoul(ptrs[2], &dummy, 16);
  357.     else
  358.         startAddr = 0;
  359.  
  360.     if(ptrs[3] != NULL)
  361.         endAddr= strtoul(ptrs[2], &dummy, 16);
  362.     else
  363.         endAddr = startAddr + 0x80;
  364.  
  365.     if(startAddr == 0)
  366.         return -1;
  367.  
  368.     for( ; startAddr < endAddr; startAddr += 16) {
  369.         int len;
  370.  
  371.         len = (startAddr - endAddr) > 16 ? 16 : startAddr - endAddr;
  372.         debugBuffer.Len    = len;
  373.         debugBuffer.Addr   = startAddr;
  374.         debugBuffer.Buffer = (ULONG) data;
  375.         if(DispatchCommand(DBG_C_ReadMemBuf) != DBG_N_Success)
  376.         return -1;
  377.  
  378.         hexdump(data, len, buff);
  379.         fprintf(logFile, "%08x %s\n", startAddr, buff);
  380.     }
  381.         return -1;
  382.     }
  383.  
  384.     /*
  385.     ** Dump/Display registers.
  386.     */
  387.     if(cmd == 'r') {
  388.     return CommandRegister(ptrs);
  389.     }
  390.  
  391.     /*
  392.     ** Talk to the breakpoint module to do the breakpoints.
  393.     */
  394.     if(cmd == 't')
  395.     return DBG_C_SStep;
  396.  
  397.     if(cmd == 'b')
  398.     return CommandBreakpoint(ptrs);
  399.  
  400.     if(cmd == 'g')
  401.     return CommandGo(ptrs);
  402.  
  403.     if(cmd == 'p')
  404.     return CommandStep(ptrs);
  405.  
  406.     /*
  407.     ** Source/Assembler view options.
  408.     */
  409.     if(cmd == 'v')
  410.     return CommandView(ptrs);
  411.  
  412.     if(cmd == 'u')
  413.     return CommandUnassemble(ptrs);
  414.  
  415.     if(cmd == 's')
  416.     return CommandSource(ptrs);
  417.  
  418.     return -1;
  419. }
  420.  
  421. /*
  422. ** Parse the command line
  423. */
  424. int parseCommandLine(int argc, char **argv)
  425. {
  426. int i;
  427.  
  428.     for(i=1; i<argc; i++) {
  429.     if(argv[i][0] != '-')
  430.         return i;
  431.     switch(tolower(argv[i][1])) {
  432.         case 'l':    if(argv[i][2] == 0)
  433.                 return -i;
  434.             strcpy(logFileName, &argv[i][2]);
  435.             break;
  436.     }
  437.     }
  438.     return argc;
  439. }
  440.  
  441. /*
  442. ** Main entrance routine.
  443. */
  444. int main(int argc, char **argv);
  445. int main(int argc, char **argv)
  446. {
  447. static char sourceName[CCHMAXPATH];
  448. static char funcName[MAX_FUNCNAME];
  449. int nextState;                  /* Desired next state.                  */
  450. int live = 1;                   /* Whether the debugger is still alive  */
  451. int lastState = DBG_N_Success;
  452. ULONG lineNum;
  453.  
  454.     /*
  455.     ** Put up the banner.
  456.     */
  457.     fprintf(stderr, "SHERLOCK - Copyright 1992, 1993, 1994, 1994\n");
  458.     fprintf(stderr, " Version 1.1\n");
  459.     fprintf(stderr, " Harfmann Software\n");
  460.     fprintf(stderr, " All rights reserved.\n");
  461.  
  462.     /*
  463.     ** Parse the command line.
  464.     */
  465.     nextState = parseCommandLine(argc, argv);
  466.     if(nextState < 0) {
  467.     fprintf(stderr, "Command line error: '%s'\n", argv[-nextState]);
  468.     exit(1);
  469.     }
  470.     if(argc == nextState) {
  471.     fprintf(stderr, "Usage: %s [-l] test.exe <parm1 parm2 ...>\n", argv[0]);
  472.     exit(0);
  473.     }
  474.  
  475.     /*
  476.     ** Either assign stderr to the output log file or open a log file.
  477.     */
  478. #ifdef SHERLOCK
  479.     logFile = fopen(logFileName, "w");
  480.     if(logFile == NULL) {
  481.     fprintf(stderr, "Unable to open log file\n");
  482.     exit(1);
  483.     }
  484. #else
  485.     logFile = stderr;
  486. #endif
  487.  
  488.     /*
  489.     ** Set up an exit list processor to make sure that everything gets
  490.     ** cleaned up.
  491.     */
  492.     DosExitList(EXLST_ADD, (PFNEXITLIST) Cleanup);
  493.     signal(SIGBREAK, breakHandler);
  494.     signal(SIGINT,   breakHandler);
  495.  
  496.     /*
  497.     ** Start and connect to the debuggee.
  498.     */
  499.     StartProgram(argc, argv, nextState);
  500.     debugBuffer.Pid = debugInfo.pid;
  501.     debugBuffer.Tid = 0;
  502.     debugBuffer.Value = DBG_L_386;
  503.     if(DispatchCommand(DBG_C_Connect) != DBG_N_Success) {
  504.     fprintf(logFile, "Unable to connect!\n");
  505.         exit(1);
  506.     }
  507.     debugBuffer.Tid = 0;
  508.     if(DispatchCommand(DBG_C_SStep) != DBG_N_Exception) {
  509.     fprintf(logFile, "Unable to single step to force DLL load!\n");
  510.         exit(1);
  511.     }
  512.     debugBuffer.Value = XCPT_CONTINUE_STOP;
  513.     if(DispatchCommand(DBG_C_Continue) != DBG_N_Success) {
  514.     fprintf(logFile, "Unable to continue after single step!\n");
  515.         exit(1);
  516.     }
  517. #ifndef SHERLOCK
  518.     DosSelectSession(0);
  519. #endif
  520.  
  521.     /*
  522.     ** Starting with the connect command, dispatch a command and then
  523.     ** get the next command.
  524.     */
  525.     setjmp(JumpBuff);
  526.     while(live) {
  527.  
  528.         /*
  529.         ** Get the next command.
  530.     */
  531.     do {
  532. #ifdef SHERLOCK
  533.         if(lastState == DBG_N_ProcTerm)
  534.         goto ExitDebugger;
  535.  
  536.         strcpy(buff, "g");
  537.         ptrs[0] = buff;
  538.         ptrs[1] = ptrs[0];
  539. #else
  540.         GetCommand(buff, sizeof(buff), ptrs, MAX_ARGS);
  541. #endif
  542.             nextState = DoCommand(ptrs);
  543.  
  544.             /*
  545.             ** If the next state is 666, die.
  546.             */
  547.         if(nextState == 666)
  548.         goto ExitDebugger;
  549.  
  550.         } while(nextState <= 0);
  551.  
  552.         /*
  553.         ** Dispatch the command and then dump the stack and registers.
  554.         **
  555.         ** Depending upon the state of the dispatched message, print
  556.         ** the appropriate message.
  557.         */
  558. commandRestart:
  559.     switch(lastState = DispatchCommand(nextState)) {
  560.             case DBG_N_Success:     /* Successful command completion    */
  561.         fprintf(logFile, "Success\n");
  562.                 break;
  563.  
  564.             case DBG_N_Error:       /* Error detected during command    */
  565.         fprintf(logFile, "Command ERROR\n");
  566.         fprintf(logFile, "Command: %d ILLEGAL\n", nextState);
  567.                 live = 0;
  568.         break;
  569.  
  570.         case DBG_N_Exception:   /* Exception detected   */
  571.         if(HandleException(nextState))
  572.                     goto commandRestart;
  573. #ifdef SHERLOCK
  574.         dumpExceptionData();
  575. #endif
  576.         break;
  577.  
  578.             case DBG_N_CoError:     /* Coprocessor not in use error */
  579.         fprintf(logFile, "Coprocess Error\n");
  580.                 live = 0;
  581.         break;
  582.  
  583.         case DBG_N_ThreadCreate: {    /* Thread creation  */
  584.             struct ThreadList  *link, *next;
  585.  
  586.             next = malloc(sizeof(struct ThreadList));
  587.             next->next = NULL;
  588.             next->tid  = debugBuffer.Tid;
  589.             for(link = &threadList;
  590.             link && link->next;
  591.             link = link->next)
  592.                 ;
  593.             link->next = next;
  594.             fprintf(logFile, "Thread Created\n");
  595.             break;
  596.         }
  597.  
  598.         case DBG_N_ThreadTerm: {    /* Thread termination - not in DosExitList  */
  599.             struct ThreadList *link, *prior;
  600.  
  601.             prior = NULL;
  602.             for(link=&threadList; link; link=link->next) {
  603.             if(link->tid == debugBuffer.Tid) {
  604.                 if(prior) {
  605.                 prior->next = link->next;
  606.                 free(link);
  607.                 }
  608.                 break;
  609.             }
  610.             prior = link;
  611.             }
  612.             fprintf(logFile, "Thread %d Terminated\n", debugBuffer.Tid);
  613.             break;
  614.         }
  615.  
  616.             case DBG_N_AsyncStop:   /* Async Stop detected  */
  617.         fprintf(logFile, "ASync Stop\n");
  618.                 live = 0;
  619.         break;
  620.  
  621.         case DBG_N_ProcTerm:    /* Process termination - DosExitList done    */
  622.         fprintf(logFile, "Process Terminated\n");
  623.         break;
  624.  
  625.         case DBG_N_NewProc:     /* New Process started  */
  626.         fprintf(logFile, "New Process Stated\n");
  627.                 break;
  628.  
  629.         case DBG_N_AliasFree:   /* Alias needs to be freed    */
  630.         fprintf(logFile, "Alias needs to be freed\n");
  631.                 live = 0;
  632.         break;
  633.  
  634.             case DBG_N_Watchpoint:  /* Watchpoint hit   */
  635.         fprintf(logFile, "Watch point hit\n");
  636.         if(!isValidBreakpoint(Linearize(debugBuffer.EIP, debugBuffer.CS)))
  637.             fprintf(logFile, "UNKNOWN WATCHPOINT HIT!\n");
  638.                 break;
  639.  
  640.         case DBG_N_ModuleFree:  /* Module freed        */
  641.         fprintf(logFile, "Module Freed\n");
  642.         break;
  643.  
  644.             case DBG_N_RangeStep:   /* Range Step detected  */
  645.         fprintf(logFile, "Range Step\n");
  646.                 break;
  647.     }
  648.  
  649. #ifndef SHERLOCK
  650.         {
  651.             DebugModule *module;
  652.             char *mod;
  653.  
  654.         DispatchCommand(DBG_C_ReadReg);
  655.         debugBuffer.Addr = Linearize(debugBuffer.EIP, debugBuffer.CS);
  656.         DispatchCommand(DBG_C_AddrToObject);
  657.             if((debugBuffer.Cmd == DBG_N_Success) && (debugBuffer.Value & 0x10000000)) {
  658.                 module = FindModule(debugBuffer.MTE, NULL);
  659.                 if(module == NULL)
  660.                     mod = "UNKNOWN";
  661.                 else
  662.                     mod = module->name;
  663.         FindSource(module, Linearize(debugBuffer.EIP, debugBuffer.CS),
  664.                funcName, sourceName, &lineNum);
  665.         fprintf(logFile, "EIP: %08x, DLL: %s Func: %s\n",
  666.             Linearize(debugBuffer.EIP, debugBuffer.CS), mod, funcName);
  667.         DisplaySource(module, sourceName, lineNum);
  668.         }
  669.     }
  670. #endif
  671.     DumpWatchpoints();
  672.     }
  673. #ifndef SHERLOCK
  674.     dumpPStat();
  675. #endif
  676. ExitDebugger:
  677.     return 0;
  678. }
  679.