doom3-gpl
Doom 3 GPL source release
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
DebuggerServer.cpp
Go to the documentation of this file.
1 /*
2 ===========================================================================
3 
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
6 
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
8 
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13 
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
21 
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
23 
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25 
26 ===========================================================================
27 */
28 
29 #include "../../idlib/precompiled.h"
30 #pragma hdrstop
31 
32 #include "../../game/gamesys/Event.h"
33 #include "../../game/gamesys/Class.h"
34 #include "../../game/script/Script_Program.h"
35 #include "../../game/script/Script_Interpreter.h"
36 #include "../../game/script/Script_Thread.h"
37 #include "../../game/script/Script_Compiler.h"
38 #include "../../framework/sync/Msg.h"
39 #include "DebuggerApp.h"
40 #include "DebuggerServer.h"
41 
42 /*
43 ================
44 rvDebuggerServer::rvDebuggerServer
45 ================
46 */
48 {
49  mConnected = false;
50  mBreakNext = false;
51  mBreak = false;
52  mBreakStepOver = false;
53  mBreakStepInto = false;
54  mGameThread = NULL;
55  mLastStatementLine = -1;
58 }
59 
60 /*
61 ================
62 rvDebuggerServer::~rvDebuggerServer
63 ================
64 */
66 {
67 }
68 
69 /*
70 ================
71 rvDebuggerServer::Initialize
72 
73 Initialize the debugger server. This function should be called before the
74 debugger server is used.
75 ================
76 */
78 {
79  // Initialize the network connection
80  if ( !mPort.InitForPort ( 27980 ) )
81  {
82  return false;
83  }
84 
85  // Get a copy of the game thread handle so we can suspend the thread on a break
86  DuplicateHandle ( GetCurrentProcess(), GetCurrentThread ( ), GetCurrentProcess(), &mGameThread, 0, FALSE, DUPLICATE_SAME_ACCESS );
87 
88  // Create a critical section to ensure that the shared thread
89  // variables are protected
90  InitializeCriticalSection ( &mCriticalSection );
91 
92  // Server must be running on the local host on port 28980
93  Sys_StringToNetAdr ( "localhost", &mClientAdr, true );
94  mClientAdr.port = 27981;
95 
96  // Attempt to let the server know we are here. The server may not be running so this
97  // message will just get ignored.
99 
100  return true;
101 }
102 
103 void rvDebuggerServer::OSPathToRelativePath( const char *osPath, idStr &qpath )
104 {
105  if ( strchr( osPath, ':' ) )
106  {
107  qpath = fileSystem->OSPathToRelativePath( osPath );
108  }
109  else
110  {
111  qpath = osPath;
112  }
113 }
114 
115 /*
116 ================
117 rvDebuggerServer::Shutdown
118 
119 Shutdown the debugger server.
120 ================
121 */
123 {
124  // Let the debugger client know we are shutting down
125  if ( mConnected )
126  {
128  mConnected = false;
129  }
130 
131  mPort.Close();
132 
133  // dont need the crit section anymore
134  DeleteCriticalSection ( &mCriticalSection );
135 }
136 
137 /*
138 ================
139 rvDebuggerServer::ProcessMessages
140 
141 Process all incoming network messages from the debugger client
142 ================
143 */
145 {
146  netadr_t adrFrom;
147  msg_t msg;
149 
150  MSG_Init( &msg, buffer, sizeof( buffer ) );
151 
152  // Check for pending udp packets on the debugger port
153  while ( mPort.GetPacket ( adrFrom, msg.data, msg.cursize, msg.maxsize ) )
154  {
155  unsigned short command;
156 
157  // Only accept packets from the debugger server for security reasons
158  if ( !Sys_CompareNetAdrBase ( adrFrom, mClientAdr ) )
159  {
160  continue;
161  }
162 
163  command = (unsigned short) MSG_ReadShort ( &msg );
164 
165  switch ( command )
166  {
167  case DBMSG_CONNECT:
168  mConnected = true;
170  break;
171 
172  case DBMSG_CONNECTED:
173  mConnected = true;
174  break;
175 
176  case DBMSG_DISCONNECT:
177  ClearBreakpoints ( );
178  Resume ( );
179  mConnected = false;
180  break;
181 
182  case DBMSG_ADDBREAKPOINT:
183  HandleAddBreakpoint ( &msg );
184  break;
185 
187  HandleRemoveBreakpoint ( &msg );
188  break;
189 
190  case DBMSG_RESUME:
191  Resume ( );
192  break;
193 
194  case DBMSG_BREAK:
195  mBreakNext = true;
196  break;
197 
198  case DBMSG_STEPOVER:
199  mBreakStepOver = true;
203  {
205  }
206  else
207  {
209  }
210  Resume ( );
211  break;
212 
213  case DBMSG_STEPINTO:
214  mBreakStepInto = true;
215  Resume ( );
216  break;
217 
219  HandleInspectVariable ( &msg );
220  break;
221 
223  HandleInspectCallstack ( &msg );
224  break;
225 
227  HandleInspectThreads ( &msg );
228  break;
229  }
230  }
231 
232  return true;
233 }
234 
235 /*
236 ================
237 rvDebuggerServer::SendMessage
238 
239 Send a message with no data to the debugger server.
240 ================
241 */
243 {
244  msg_t msg;
246 
247  MSG_Init( &msg, buffer, sizeof( buffer ) );
248  MSG_WriteShort ( &msg, (int)dbmsg );
249 
250  SendPacket ( msg.data, msg.cursize );
251 }
252 
253 /*
254 ================
255 rvDebuggerServer::HandleAddBreakpoint
256 
257 Handle the DBMSG_ADDBREAKPOINT message being sent by the debugger client. This
258 message is handled by adding a new breakpoint to the breakpoint list with the
259 data supplied in the message.
260 ================
261 */
263 {
264  bool onceOnly = false;
265  long lineNumber;
266  long id;
267  char filename[MAX_PATH];
268 
269  // Read the breakpoint info
270  onceOnly = MSG_ReadBits ( msg, 1 ) ? true : false;
271  lineNumber = MSG_ReadLong ( msg );
272  id = MSG_ReadLong ( msg );
273 
274  MSG_ReadString ( msg, filename, MAX_PATH );
275 
276  // Since breakpoints are used by both threads we need to
277  // protect them with a crit section
278  EnterCriticalSection ( &mCriticalSection );
279  mBreakpoints.Append ( new rvDebuggerBreakpoint ( filename, lineNumber, id ) );
280  LeaveCriticalSection ( &mCriticalSection );
281 }
282 
283 /*
284 ================
285 rvDebuggerServer::HandleRemoveBreakpoint
286 
287 Handle the DBMSG_REMOVEBREAKPOINT message being sent by the debugger client. This
288 message is handled by removing the breakpoint that matches the given id from the
289 list.
290 ================
291 */
293 {
294  int i;
295  int id;
296 
297  // ID that we are to remove
298  id = MSG_ReadLong ( msg );
299 
300  // Since breakpoints are used by both threads we need to
301  // protect them with a crit section
302  EnterCriticalSection ( &mCriticalSection );
303 
304  // Find the breakpoint that matches the given id and remove it from the list
305  for ( i = 0; i < mBreakpoints.Num(); i ++ )
306  {
307  if ( mBreakpoints[i]->GetID ( ) == id )
308  {
309  delete mBreakpoints[i];
311  break;
312  }
313  }
314 
315  LeaveCriticalSection ( &mCriticalSection );
316 }
317 
318 /*
319 ================
320 rvDebuggerServer::MSG_WriteCallstackFunc
321 
322 Writes a single callstack entry to the given message
323 ================
324 */
325 void rvDebuggerServer::MSG_WriteCallstackFunc ( msg_t* msg, const prstack_t* stack )
326 {
327  const statement_t* st;
328  const function_t* func;
329 
330  func = stack->f;
331 
332  // If the function is unknown then just fill in with default data.
333  if ( !func )
334  {
335  MSG_WriteString ( msg, "<UNKNOWN>" );
336  MSG_WriteString ( msg, "<UNKNOWN>" );
337  MSG_WriteLong ( msg, 0 );
338  return;
339  }
340  else
341  {
342  MSG_WriteString ( msg, va("%s( ??? )", func->Name() ) );
343  }
344 
345  // Use the calling statement as the filename and linenumber where
346  // the call was made from
347  st = &mBreakProgram->GetStatement ( stack->s );
348  if ( st )
349  {
350  idStr qpath;
352  qpath.BackSlashesToSlashes ( );
353  MSG_WriteString ( msg, qpath );
354  MSG_WriteLong ( msg, st->linenumber );
355  }
356  else
357  {
358  MSG_WriteString ( msg, "<UNKNOWN>" );
359  MSG_WriteLong ( msg, 0 );
360  }
361 }
362 
363 /*
364 ================
365 rvDebuggerServer::HandleInspectCallstack
366 
367 Handle an incoming inspect callstack message by sending a message
368 back to the client with the callstack data.
369 ================
370 */
372 {
373  msg_t msg;
375  int i;
376  prstack_t temp;
377 
378  MSG_Init( &msg, buffer, sizeof( buffer ) );
379  MSG_WriteShort ( &msg, (int)DBMSG_INSPECTCALLSTACK );
380 
381  MSG_WriteShort ( &msg, (int)mBreakInterpreter->GetCallstackDepth ( ) );
382 
383  // write out the current function
385  temp.s = 0;
386  temp.stackbase = 0;
387  MSG_WriteCallstackFunc ( &msg, &temp );
388 
389  // Run through all of the callstack and write each to the msg
390  for ( i = mBreakInterpreter->GetCallstackDepth ( ) - 1; i > 0; i -- )
391  {
393  }
394 
395  SendPacket ( msg.data, msg.cursize );
396 }
397 
398 /*
399 ================
400 rvDebuggerServer::HandleInspectThreads
401 
402 Send the list of the current threads in the interpreter back to the debugger client
403 ================
404 */
406 {
407  msg_t msg;
409  int i;
410 
411  // Initialize the message
412  MSG_Init( &msg, buffer, sizeof( buffer ) );
413  MSG_WriteShort ( &msg, (int)DBMSG_INSPECTTHREADS );
414 
415  // Write the number of threads to the message
416  MSG_WriteShort ( &msg, (int)idThread::GetThreads().Num() );
417 
418  // Loop through all of the threads and write their name and number to the message
419  for ( i = 0; i < idThread::GetThreads().Num(); i ++ )
420  {
421  idThread* thread = idThread::GetThreads()[i];
422 
423  MSG_WriteString ( &msg, thread->GetThreadName ( ) );
424  MSG_WriteLong ( &msg, thread->GetThreadNum ( ) );
425 
426  MSG_WriteBits ( &msg, (int)(thread == mBreakInterpreter->GetThread ( )), 1 );
427  MSG_WriteBits ( &msg, (int)thread->IsDoneProcessing(), 1 );
428  MSG_WriteBits ( &msg, (int)thread->IsWaiting(), 1 );
429  MSG_WriteBits ( &msg, (int)thread->IsDying(), 1 );
430  }
431 
432  // Send off the inspect threads packet to the debugger client
433  SendPacket ( msg.data, msg.cursize );
434 }
435 
436 /*
437 ================
438 rvDebuggerServer::HandleInspectVariable
439 
440 Respondes to a request from the debugger client to inspect the value of a given variable
441 ================
442 */
444 {
445  char varname[256];
446  int scopeDepth;
447 
448  if ( !mBreak )
449  {
450  return;
451  }
452 
453  scopeDepth = (short)MSG_ReadShort ( in_msg );
454  MSG_ReadString ( in_msg, varname, 256 );
455 
456  idStr varvalue;
457 
458  msg_t msg;
460 
461  // Initialize the message
462  MSG_Init( &msg, buffer, sizeof( buffer ) );
463  MSG_WriteShort ( &msg, (int)DBMSG_INSPECTVARIABLE );
464 
465  if ( !mBreakInterpreter->GetRegisterValue ( varname, varvalue, scopeDepth ) )
466  {
467  varvalue = "???";
468  }
469 
470  MSG_WriteShort ( &msg, (short)scopeDepth );
471  MSG_WriteString ( &msg, varname );
472  MSG_WriteString ( &msg, varvalue );
473 
474  SendPacket ( msg.data, msg.cursize );
475 }
476 
477 /*
478 ================
479 rvDebuggerServer::CheckBreakpoints
480 
481 Check to see if any breakpoints have been hit. This includes "break next",
482 "step into", and "step over" break points
483 ================
484 */
485 void rvDebuggerServer::CheckBreakpoints ( idInterpreter* interpreter, idProgram* program, int instructionPointer )
486 {
487  const statement_t* st;
488  const char* filename;
489  int i;
490 
491  if ( !mConnected ) {
492  return;
493  }
494 
495  // Grab the current statement and the filename that it came from
496  st = &program->GetStatement ( instructionPointer );
497  filename = program->GetFilename ( st->file );
498 
499  // Operate on lines, not statements
500  if ( mLastStatementLine == st->linenumber && mLastStatementFile == st->file )
501  {
502  return;
503  }
504 
505  // Save the last visited line and file so we can prevent
506  // double breaks on lines with more than one statement
507  mLastStatementFile = idStr( st->file );
509 
510  // Reset stepping when the last function on the callstack is returned from
511  if ( st->op == OP_RETURN && interpreter->GetCallstackDepth ( ) <= 1 )
512  {
513  mBreakStepOver = false;
514  mBreakStepInto = false;
515  }
516 
517  // See if we are supposed to break on the next script line
518  if ( mBreakNext )
519  {
520  Break ( interpreter, program, instructionPointer );
521  return;
522  }
523 
524  // Only break on the same callstack depth and thread as the break over
525  if ( mBreakStepOver )
526  {
527  if ( ( interpreter->GetCurrentFunction ( ) == mBreakStepOverFunc1 ||
528  interpreter->GetCurrentFunction ( ) == mBreakStepOverFunc2 )&&
529  ( interpreter->GetCallstackDepth ( ) <= mBreakStepOverDepth ) )
530  {
531  Break ( interpreter, program, instructionPointer );
532  return;
533  }
534  }
535 
536  // See if we are supposed to break on the next line
537  if ( mBreakStepInto )
538  {
539  // Break
540  Break ( interpreter, program, instructionPointer );
541  return;
542  }
543 
544  idStr qpath;
545  OSPathToRelativePath(filename,qpath);
546  qpath.BackSlashesToSlashes ( );
547 
548  EnterCriticalSection ( &mCriticalSection );
549 
550  // Check all the breakpoints
551  for ( i = 0; i < mBreakpoints.Num ( ); i ++ )
552  {
554 
555  // Skip if not match of the line number
556  if ( st->linenumber != bp->GetLineNumber ( ) )
557  {
558  continue;
559  }
560 
561  // Skip if no match of the filename
562  if ( idStr::Icmp ( bp->GetFilename(), qpath ) )
563  {
564  continue;
565  }
566 
567  // Pop out of the critical section so we dont get stuck
568  LeaveCriticalSection ( &mCriticalSection );
569 
570  // We hit a breakpoint, so break
571  Break ( interpreter, program, instructionPointer );
572 
573  // Back into the critical section since we are going to have to leave it
574  EnterCriticalSection ( &mCriticalSection );
575 
576  break;
577  }
578 
579  LeaveCriticalSection ( &mCriticalSection );
580 }
581 
582 /*
583 ================
584 rvDebuggerServer::Break
585 
586 Halt execution of the game threads and inform the debugger client that
587 the game has been halted
588 ================
589 */
590 void rvDebuggerServer::Break ( idInterpreter* interpreter, idProgram* program, int instructionPointer )
591 {
592  msg_t msg;
594  const statement_t* st;
595  const char* filename;
596 
597  // Clear all the break types
598  mBreakStepOver = false;
599  mBreakStepInto = false;
600  mBreakNext = false;
601 
602  // Grab the current statement and the filename that it came from
603  st = &program->GetStatement ( instructionPointer );
604  filename = program->GetFilename ( st->file );
605 
606  idStr qpath;
607  OSPathToRelativePath(filename, qpath);
608  qpath.BackSlashesToSlashes ( );
609 
610  // Give the mouse cursor back to the world
611  Sys_GrabMouseCursor( false );
612 
613  // Set the break variable so we know the main thread is stopped
614  mBreak = true;
616  mBreakInterpreter = interpreter;
617  mBreakInstructionPointer = instructionPointer;
618 
619  // Inform the debugger of the breakpoint hit
620  MSG_Init( &msg, buffer, sizeof( buffer ) );
621  MSG_WriteShort ( &msg, (int)DBMSG_BREAK );
622  MSG_WriteLong ( &msg, st->linenumber );
623  MSG_WriteString ( &msg, qpath );
624  SendPacket ( msg.data, msg.cursize );
625 
626  // Suspend the game thread. Since this will be called from within the main game thread
627  // execution wont return until after the thread is resumed
628  SuspendThread ( mGameThread );
629 
630  // Let the debugger client know that we have started back up again
632 
633  // This is to give some time between the keypress that
634  // told us to resume and the setforeground window. Otherwise the quake window
635  // would just flash
636  Sleep ( 150 );
637 
638  // Bring the window back to the foreground
639  SetForegroundWindow ( win32.hWnd );
640  SetActiveWindow ( win32.hWnd );
641  UpdateWindow ( win32.hWnd );
642  SetFocus ( win32.hWnd );
643 
644  // Give the mouse cursor back to the game
645  Sys_GrabMouseCursor( true );
646 
647  // Clear all commands that were generated before we went into suspended mode. This is
648  // to ensure we dont have mouse downs with no ups because the context was changed.
650 }
651 
652 /*
653 ================
654 rvDebuggerServer::Resume
655 
656 Resume execution of the game.
657 ================
658 */
660 {
661  // Cant resume if not paused
662  if ( !mBreak )
663  {
664  return;
665  }
666 
667  mBreak = false;
668 
669  // Start the game thread back up
670  ResumeThread ( mGameThread );
671 }
672 
673 /*
674 ================
675 rvDebuggerServer::ClearBreakpoints
676 
677 Remove all known breakpoints
678 ================
679 */
681 {
682  int i;
683 
684  for ( i = 0; i < mBreakpoints.Num(); i ++ )
685  {
686  delete mBreakpoints[i];
687  }
688 
689  mBreakpoints.Clear ( );
690 }
691 
692 /*
693 ================
694 rvDebuggerServer::Print
695 
696 Sends a console print message over to the debugger client
697 ================
698 */
699 void rvDebuggerServer::Print ( const char* text )
700 {
701  if ( !mConnected )
702  {
703  return;
704  }
705 
706  msg_t msg;
708 
709  MSG_Init( &msg, buffer, sizeof( buffer ) );
710  MSG_WriteShort ( &msg, (int)DBMSG_PRINT );
711  MSG_WriteString ( &msg, text );
712 
713  SendPacket ( msg.data, msg.cursize );
714 }
void HandleRemoveBreakpoint(msg_t *msg)
const function_t * GetCurrentFunction(void) const
void Close()
Definition: posix_net.cpp:474
void HandleInspectVariable(msg_t *msg)
statement_t & GetStatement(int index)
const function_t * mBreakStepOverFunc1
idProgram * mBreakProgram
const char * GetThreadName(void)
static void ClearStates(void)
Definition: KeyInput.cpp:746
bool Sys_CompareNetAdrBase(const netadr_t a, const netadr_t b)
Definition: posix_net.cpp:248
unsigned short port
Definition: sys_public.h:407
idFileSystem * fileSystem
Definition: FileSystem.cpp:500
const function_t * f
bool Sys_StringToNetAdr(const char *s, netadr_t *a, bool doDNSResolve)
Definition: posix_net.cpp:171
void Print(const char *text)
void Sleep(const int time)
int i
Definition: process.py:33
const char * Name(void) const
const function_t * mBreakStepOverFunc2
bool Initialize(void)
int Icmp(const char *text) const
Definition: Str.h:667
void SendPacket(void *data, int datasize)
idStr & BackSlashesToSlashes(void)
Definition: Str.cpp:727
void HandleInspectThreads(msg_t *msg)
unsigned short op
bool GetPacket(netadr_t &from, void *data, int &size, int maxSize)
Definition: posix_net.cpp:487
int GetThreadNum(void)
bool IsDoneProcessing(void)
const char * GetFilename(int num)
idList< rvDebuggerBreakpoint * > mBreakpoints
GLuint program
Definition: glext.h:3473
#define NULL
Definition: Lib.h:88
GLuint buffer
Definition: glext.h:3108
bool IsDying(void)
CRITICAL_SECTION mCriticalSection
bool InitForPort(int portNumber)
Definition: posix_net.cpp:598
const int MAX_MSGLEN
Definition: DebuggerApp.h:52
void OSPathToRelativePath(const char *osPath, idStr &qpath)
static idList< idThread * > & GetThreads(void)
void HandleAddBreakpoint(msg_t *msg)
void SendMessage(EDebuggerMessage dbmsg)
void CheckBreakpoints(idInterpreter *interpreter, idProgram *program, int instructionPointer)
bool GetRegisterValue(const char *name, idStr &out, int scopeDepth)
const char * GetFilename(void)
void ClearBreakpoints(void)
void HandleInspectCallstack(msg_t *msg)
bool IsWaiting(void)
idThread * GetThread(void) const
int Append(const type &obj)
Definition: List.h:646
bool ProcessMessages(void)
unsigned short linenumber
GLuint id
Definition: glext.h:3103
int Num(void) const
Definition: List.h:265
bool RemoveIndex(int index)
Definition: List.h:849
unsigned char byte
Definition: Lib.h:75
GLfloat * st
Definition: qgl.h:89
Definition: Str.h:116
unsigned short file
#define FALSE
Definition: mprintf.c:70
int GetCallstackDepth(void) const
char * va(const char *fmt,...)
Definition: Str.cpp:1568
Win32Vars_t win32
Definition: win_main.cpp:65
void Break(idInterpreter *interpreter, idProgram *program, int instructionPointer)
virtual const char * OSPathToRelativePath(const char *OSPath)=0
const prstack_t * GetCallstack(void) const
idInterpreter * mBreakInterpreter
void MSG_WriteCallstackFunc(msg_t *msg, const prstack_t *stack)
EDebuggerMessage
void Clear(void)
Definition: List.h:184
void Sys_GrabMouseCursor(bool)
Definition: dedicated.cpp:43