Jump to content
  • Advertisement
Sign in to follow this  
Drew_Benton

Unity A Useful Debugging Tool?

This topic is 4724 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Preface: I've had this idea of a debugging tool for some time, my post from Feb 2nd that I'm just now getting around to [lol], and I'm just now thinking of doing it. However, I wan to see what you guys think and if the idea is there or not. The idea is real time debugging and tracking of variables in a client program by a host program. This is how it will work. Client Program: The client program is your program that you want to debug. There will be a class that wraps up all of the debugging stuff. For any variable that you want to track in realtime, you will register it's address and size to the class. You can assign it a name as well to know what it is. So for an example of syntax for registering an int variable: RegisterVariable( "variable", &variable, sizeof(int) );. Then when you want tracking to stop, UnregisterVariable( "variable" ); The point of having the unregister is so when the variable goes out of scope, you will not want to be reading that memory. Host Program: The host program is the debugging program that you will have access to the variables that you register in the client program. You first have to call a 'Start' function that you pass in the client programs window caption, class, as well as the file generated on the client side with all of the information for debugging. After that, it's just a matter of calling a 'Track' function with the name of the variable you want to track, and then you can see what the value's are. Addition: The idea will be the host program can have a nice neat GUI that foramts the data from the client program, so you basically have a window with edit boxes that show the current values of the tracked variables. More functionality can be added, such as full tracking of variable changes and stuff like that. Would this be a tool that would serve a good purpose, or would it be more trouble than it's worth? Any thoughts or comments on this idea? Thanks for your time! [Edited by - Drew_Benton on June 14, 2005 10:52:11 PM]

Share this post


Link to post
Share on other sites
Advertisement
When you talk about the information needed for debugging from the client side, do you mean the data you created by registering your variables, or the symbol table for the application? The address of the variable will not be enough on its own since your programs reside in different address spaces. If you are using VS .NET, the DbgHelp library is invaluable for this type of work (SymFromAddr is a fantastic function if you have a handle to the process). It sounds like an awesome idea, and I hope it works out. I hope this helps at least a little.

Scott

Share this post


Link to post
Share on other sites
For some, I could see its worth. For some, it might be easier to just do it in their own application.

Though, a few questions.

I'm not as familiar with Boost as I perhaps should be, so this might be moot, but... I have a fairly, eh, kludgey let's say, serialization/lexical_cast setup. To my knowledge, something similar would be needed for non-trivial variable display. How do you intend on handling [edit: non-trivial variables, such as large-ish classes]?

I'm not as familiar with some concurrency things as I probably should be, but how do you intend to actually track the values? I imagine not many apps are made to be randomly interrupted by another thread/process. Will the debugging app supply its own memory manager perhaps?

Share this post


Link to post
Share on other sites
Quote:
Original post by scott_l_smith
When you talk about the information needed for debugging from the client side, do you mean the data you created by registering your variables, or the symbol table for the application?


The data needed for debugging is just 3 things: a std::string for the user name for the variable, an 'int' that tells the address of the variable, and an 'int' that tells the size of the memory. Yes, right now that's not enough, since if I want to actually show data, I need to have one more 'int' that stores the 'type', so I know what to output.

Quote:
The address of the variable will not be enough on its own since your programs reside in different address spaces.


[smile] The secret is just using ReadProcessMemory, nothing special. Part of the responsibilities of the client program is to save it's own Process ID, so when I load in that debugging info mentioned above, the host program has the PID it needs. When I pass in the class name as well as caption to the host, it will find the HWND (which now thinking about it is pointless since I can just send over the HWND from the client program)

Quote:
. If you are using VS .NET, the DbgHelp library is invaluable for this type of work (SymFromAddr is a fantastic function if you have a handle to the process). It sounds like an awesome idea, and I hope it works out. I hope this helps at least a little.


I am learning .Net on the side (C# & Windows.Forms), so thanks for that. I know the .Net has a large library with tons of useful stuff! I will definitly take a look at what it has to offer in case I want to port this to C#. Thanks for your reply!

Quote:
Original post by Telastyn
For some, I could see its worth. For some, it might be easier to just do it in their own application.


Yea, that's what I'd like to find out. I know using the debugger is pretty useful when going though stuff, but sometimes, I'd be nice to be able to see more or have more control over it without having to 'break' all the time.

Quote:
I'm not as familiar with Boost as I perhaps should be, so this might be moot, but... I have a fairly, eh, kludgey let's say, serialization/lexical_cast setup. To my knowledge, something similar would be needed for non-trivial variable display. How do you intend on handling [edit: non-trivial variables, such as large-ish classes]?


Well I have no idea of Boost [wink] but for large classes, right now off the top of my head, everything will happen the same way as individual variables, just by registering them. The main reason is because this will allow for inheritance and polymorphism to be used. If you have any virtual functions in your class, then you cannot serilize it easily with just a simply fwrite as you can with most C structs. Perhaps some advance C++ trickery could lend to making it more easier to automatically register a class, but that will be down the line

Quote:
I'm not as familiar with some concurrency things as I probably should be, but how do you intend to actually track the values? I imagine not many apps are made to be randomly interrupted by another thread/process. Will the debugging app supply its own memory manager perhaps?


Well actually, I have not seen any problems, but maybe I need something more larger scale to see if there will be problems with access. I mean right now it just does a ReadProcessMemory and get's the value. 'Theoretically' it would be impossible for a concurrent access on the same memory address, but with technology such as HyperThreading and Dual Cores now, I will have to make sure that's not a problem. Right now I'm just on a 633mhz Celeron, so I don't have to worry about that *yet*.

Does anyone know anything about what happens if two apps try to read the same memory address at the same time? I think I may need to look into that, but right now everything works as expected. Thanks for your reply as well!

I guess I will make a little demo to show how it's working right now. Nothing GUI wise yet just console. I'll make a new post when that's done. Heck, I'll even post the code so far, go go Open Source [smile].

Share this post


Link to post
Share on other sites
Well I meant for an update earlier, but I had other stuff come up. So this example is not the best of what I had in mind, but it does show what I am talking about.

First is the awful source that will probabally get me tomatoed for using such as mixture of C/C++/STD. Anyways it's not done and I'm in the process of making it all use STD C++ for maximum efficiency and ease.

The client program represents some program that you are debugging.

Client.cpp

#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>

#include <string>
#include <algorithm>
#include <map>

struct TrackedVariable
{
int address;
int size;

TrackedVariable()
{
address = 0;
size = 0;
}

TrackedVariable( void* Address, int Size )
{
address = (int)Address;
size = Size;
}

bool operator==( const TrackedVariable &RHS )
{
return( RHS.address == address && RHS.size == size );
}
};

class RealTimeDebugClient
{
private:
int ProcessID;
typedef std::map<std::string, TrackedVariable> TrackedVariableMap;
typedef TrackedVariableMap::iterator TrackedVariableMapIterator;
TrackedVariableMap Variables;

public:
RealTimeDebugClient(DWORD pid)
{
Variables.clear();
ProcessID = pid;
}

void RegisterVariable( std::string name, void* address, int size )
{
if( Variables.find( name ) == Variables.end() )
Variables.insert( std::pair< std::string, TrackedVariable >( name, TrackedVariable( address, size ) ) );
}

void UnregisterVariable( std::string name )
{
TrackedVariableMapIterator itr = Variables.find(name);
if( itr != Variables.end() )
Variables.erase( itr );
}

void Save()
{
FILE* outFile = fopen("out.txt","w");
char buffer[256];

// Save the process ID first
memset( buffer, 0, 256 );
sprintf( buffer, "%i", ProcessID );
fwrite( buffer, 1, strlen(buffer), outFile );

// Now loop though all the variables
for( TrackedVariableMapIterator itr = Variables.begin(); itr != Variables.end(); itr++ )
{
// Save the name
memset( buffer, 0, 256 );
sprintf( buffer, "\n%s\n", itr->first.c_str() );
fwrite( buffer, 1, strlen(buffer), outFile );

// Save the address
memset( buffer, 0, 256 );
sprintf( buffer, "%i\n", itr->second.address );
fwrite( buffer, 1, strlen(buffer), outFile );

// Save the size
memset( buffer, 0, 256 );
sprintf( buffer, "%i", itr->second.size );
fwrite( buffer, 1, strlen(buffer), outFile );
}

// Close the file
fclose(outFile);
}

~RealTimeDebugClient()
{
}
};

int variable = 25;

int main( int argc, char* argv[] )
{
RealTimeDebugClient debugger( GetCurrentProcessId() );
debugger.RegisterVariable( "variable", &variable, sizeof(int) );
debugger.Save();

DWORD Time = 2500;
printf( "Process ID: %i\n\n", GetCurrentProcessId() );

printf( "Variable: %i Address: %i\n", variable, &variable );
Sleep(Time);

while(1)
{
variable = 1337;
printf( "Variable: %i Address: %i\n", variable, &variable );
Sleep(Time);

variable = 32767;
printf( "Variable: %i Address: %i\n", variable, &variable );
Sleep(Time);

variable = -2469;
printf( "Variable: %i Address: %i\n", variable, &variable );
Sleep(Time);
}

return 0;
}







The host program is the program you are using to debug the client program with. All it does right now is load up the text file the client program generates and use that information to read memory locations.


Host.cpp
#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>

#include <string>
#include <vector>

struct TrackedVariable
{
std::string name;
int address;
int size;

TrackedVariable()
{
name = "";
address = 0;
size = 0;
}

TrackedVariable( std::string Name, void* Address, int Size )
{
name = Name;
address = (int)Address;
size = Size;
}

bool operator==( const TrackedVariable &RHS )
{
return( RHS.name == name && RHS.address == address && RHS.size == size );
}
};

class RealTimeDebugHost
{
private:
HWND ClientHwnd;
int ProcessID;

std::vector<TrackedVariable> Variables;

HANDLE clientHandle;

bool deadProcess;
int strike;

std::string fileName;

public:
~RealTimeDebugHost()
{
}

RealTimeDebugHost()
{
clientHandle = 0;
deadProcess = false;
strike = 0;
}

void Start( const char* className, const char* captionName, const std::string& filename )
{
ClientHwnd = FindWindowA( className, captionName );
if( !ClientHwnd )
{
printf( "Client window cannot be found!" );
abort();
}
fileName = filename;
}

void Load( )
{
Variables.clear();

FILE* inFile = fopen( fileName.c_str(), "r" );
// Get the Process ID first, use scope rules to cut down on memory
{
char buffer[33] = {0};
fgets( buffer, 32, inFile);
ProcessID = atoi(buffer);
}

// Read in the whole file
while( !feof(inFile) )
{
TrackedVariable tempVar;

// Buffer for the data
char Buffer[257] = {0};

// Get the name
memset( Buffer, 0, 257 );
fgets( Buffer, 256, inFile);
tempVar.name = Buffer;

// Get the address
memset( Buffer, 0, 257 );
fgets( Buffer, 256, inFile);
tempVar.address = atoi(Buffer);

// Get the size
memset( Buffer, 0, 257 );
fgets( Buffer, 256, inFile);
tempVar.size = atoi(Buffer);

Variables.push_back( tempVar );
}
fclose( inFile );
}

void Track( int index )
{
if( deadProcess || index >= Variables.size() )
return;

if( !clientHandle )
{
clientHandle = OpenProcess( PROCESS_ALL_ACCESS, FALSE, ProcessID );
if( !clientHandle )
{
Sleep(1000);
strike++;
if( strike > 3 )
{
deadProcess = true;
printf("Process has died...\n");
}
return;
}
}

DWORD bytesRead = 0;
int buffer = -1;
ReadProcessMemory( clientHandle, (int*)(Variables[index].address), &buffer, Variables[index].size, &bytesRead );
printf( "Name: %s Value: %i\n", Variables[index].name.c_str(), buffer );

CloseHandle( clientHandle );
clientHandle = 0;

/*DWORD bytesWrote = 0;
int* newbuffer = new int;
*newbuffer = 32767;
WriteProcessMemory( clientHandle, (int*)(ptr), newbuffer, sizeof(int), &bytesWrote );
printf( "Variable: %i\nBytesWrote: %i\n", *newbuffer, bytesWrote );*/

}
};

int main( int argc, char* argv[] )
{
RealTimeDebugHost host;
host.Start( "ConsoleWindowClass", 0, "out.txt" );
while( !kbhit() )
{
host.Track(0);
Sleep(500);
host.Load();
}
return 0;
}






A few things to note is that the host runs 2x as many times as the client, so it should update enough to catch all changes for this trivial example. However in other examples, you'd want this running in real time to get maximum precision (something I can't do on a Celeron mind you )

Alos note in the host program how I reload the data after every loop to ensure I do not read any memory locations that are non-existant. This is very important. In the client program I have the code commented out that unregisters the variable. You could probabally add in a variable for total loops and when you hit that, you call the unreg function and see how the host program handles it.

Ok that's about it. If anyone likes anything here feel free to use it how you want, improve it, make money off it [lol], etc.. Feel free to share any ideas though [grin] Here are the compiled demos in a zip file to run if you just want to see it in action. Make sure you run client first then host

Download

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!