1
0
mirror of https://github.com/rwengine/openrw.git synced 2024-11-22 02:12:45 +01:00

Improve debugger functionality and display

* Display Breakpoint information
* Current instruction is hilighted
This commit is contained in:
Daniel Evans 2015-07-07 03:48:32 +01:00
parent 4ce275c381
commit 5461f1a5bb
5 changed files with 115 additions and 71 deletions

View File

@ -46,9 +46,9 @@ void ScriptMachine::executeThread(SCMThread &t, int msPassed)
bool hasDebugging = !! bpHandler;
while( t.wakeCounter == 0 ) {
auto opcode = _file->read<SCMOpcode>(t.programCounter);
auto opcorg = opcode;
while( t.wakeCounter == 0 ) {
auto pc = t.programCounter;
auto opcode = _file->read<SCMOpcode>(pc);
bool isNegatedConditional = ((opcode & SCM_NEGATE_CONDITIONAL_MASK) == SCM_NEGATE_CONDITIONAL_MASK);
opcode = opcode & ~SCM_NEGATE_CONDITIONAL_MASK;
@ -56,13 +56,11 @@ void ScriptMachine::executeThread(SCMThread &t, int msPassed)
ScriptFunctionMeta* foundcode;
if( ! _ops->findOpcode(opcode, &foundcode) )
{
throw IllegalInstruction(opcode, t.programCounter, t.name);
throw IllegalInstruction(opcode, pc, t.name);
}
ScriptFunctionMeta& code = *foundcode;
// Keep the pc for the debugger
auto pc = t.programCounter;
t.programCounter += sizeof(SCMOpcode);
pc += sizeof(SCMOpcode);
SCMParams parameters;
@ -70,7 +68,7 @@ void ScriptMachine::executeThread(SCMThread &t, int msPassed)
auto requiredParams = std::abs(code.arguments);
for( int p = 0; p < requiredParams || hasExtraParameters; ++p ) {
auto type_r = _file->read<SCMByte>(t.programCounter);
auto type_r = _file->read<SCMByte>(pc);
auto type = static_cast<SCMType>(type_r);
if( type_r > 42 ) {
@ -78,7 +76,7 @@ void ScriptMachine::executeThread(SCMThread &t, int msPassed)
type = TString;
}
else {
t.programCounter += sizeof(SCMByte);
pc += sizeof(SCMByte);
}
parameters.push_back(SCMOpcodeParameter { type, { 0 } });
@ -87,71 +85,74 @@ void ScriptMachine::executeThread(SCMThread &t, int msPassed)
hasExtraParameters = false;
break;
case TInt8:
parameters.back().integer = _file->read<std::uint8_t>(t.programCounter);
t.programCounter += sizeof(SCMByte);
parameters.back().integer = _file->read<std::uint8_t>(pc);
pc += sizeof(SCMByte);
break;
case TInt16:
parameters.back().integer = _file->read<std::int16_t>(t.programCounter);
t.programCounter += sizeof(SCMByte) * 2;
parameters.back().integer = _file->read<std::int16_t>(pc);
pc += sizeof(SCMByte) * 2;
break;
case TGlobal: {
auto v = _file->read<std::uint16_t>(t.programCounter);
auto v = _file->read<std::uint16_t>(pc);
parameters.back().globalPtr = globalData.data() + v; //* SCM_VARIABLE_SIZE;
if( v >= _file->getGlobalsSize() )
{
state->world->logger->error("SCM", "Global Out of bounds! "+ std::to_string(v) + " " + std::to_string(_file->getGlobalsSize()));
}
t.programCounter += sizeof(SCMByte) * 2;
pc += sizeof(SCMByte) * 2;
}
break;
case TLocal: {
auto v = _file->read<std::uint16_t>(t.programCounter);
auto v = _file->read<std::uint16_t>(pc);
parameters.back().globalPtr = t.locals.data() + v * SCM_VARIABLE_SIZE;
if( v >= SCM_THREAD_LOCAL_SIZE )
{
state->world->logger->error("SCM", "Local Out of bounds!");
}
t.programCounter += sizeof(SCMByte) * 2;
pc += sizeof(SCMByte) * 2;
}
break;
case TInt32:
parameters.back().integer = _file->read<std::uint32_t>(t.programCounter);
t.programCounter += sizeof(SCMByte) * 4;
parameters.back().integer = _file->read<std::uint32_t>(pc);
pc += sizeof(SCMByte) * 4;
break;
case TString:
std::copy(_file->data()+t.programCounter, _file->data()+t.programCounter+8,
std::copy(_file->data()+pc, _file->data()+pc+8,
parameters.back().string);
t.programCounter += sizeof(SCMByte) * 8;
pc += sizeof(SCMByte) * 8;
break;
case TFloat16:
parameters.back().real = _file->read<std::int16_t>(t.programCounter) / 16.f;
t.programCounter += sizeof(SCMByte) * 2;
parameters.back().real = _file->read<std::int16_t>(pc) / 16.f;
pc += sizeof(SCMByte) * 2;
break;
default:
throw UnknownType(type, t.programCounter, t.name);
throw UnknownType(type, pc, t.name);
break;
};
}
ScriptArguments sca(&parameters, &t, this);
if( hasDebugging )
{
if( breakpoints.find(pc) != breakpoints.end() || interupt )
{
interupt = false;
SCMBreakpoint bp;
bp.pc = t.programCounter;
bp.thread = &t;
bp.vm = this;
bp.function = &code;
bp.args = &sca;
bpHandler(bp);
}
}
// After debugging has been completed, update the program counter
t.programCounter = pc;
if(code.function)
{
ScriptArguments sca(&parameters, &t, this);
if( hasDebugging )
{
if( breakpoints.find(pc) != breakpoints.end() || interupt )
{
interupt = false;
SCMBreakpoint bp;
bp.pc = pc;
bp.thread = &t;
bp.vm = this;
bp.function = &code;
bp.args = &sca;
bpHandler(bp);
}
}
{
code.function(sca);
}
@ -181,7 +182,7 @@ void ScriptMachine::executeThread(SCMThread &t, int msPassed)
t.conditionResult = (t.conditionMask != 0);
}
}
}
SCMOpcodeParameter p;
p.globalPtr = (t.locals.data() + 16 * sizeof ( SCMByte ) * 4);
@ -239,15 +240,12 @@ void ScriptMachine::execute(float dt)
{
int ms = dt * 1000.f;
for(size_t ti = 0; ti < _activeThreads.size(); ++ti) {
auto thread = _activeThreads[ti];
auto& thread = _activeThreads[ti];
executeThread( thread, ms );
if( thread.finished ) {
_activeThreads.erase( _activeThreads.begin() + ti );
}
else {
_activeThreads[ti] = thread;
}
}
}
}

View File

@ -903,6 +903,7 @@ GameModule::GameModule()
bindFunction(0x015A, game_reset_camera, 0, "Reset Camera" );
bindFunction(0x015C, game_set_zone_ped_info, 11, "Set zone ped info" );
bindUnimplemented( 0x015D, game_set_timescale;, 1, "Set Game Timescale" );
bindFunction(0x015F, game_camera_fixed_position, 6, "Set Fixed Camera Position" );
bindFunction(0x0160, game_camera_lookat_position, 4, "Point Camera at Point" );

View File

@ -293,6 +293,23 @@ bool game_player_near_point_on_foot_3D(const ScriptArguments& args)
return false;
}
bool game_player_near_point_in_vehicle_3D(const ScriptArguments& args)
{
auto character = static_cast<CharacterObject*>(args.getPlayerCharacter(0));
glm::vec3 center(args[1].real, args[2].real, args[3].real);
glm::vec3 size(args[4].real, args[5].real, args[6].real);
bool unkown = !!args[7].integer;
auto vehicle = character->getCurrentVehicle();
if( vehicle ) {
auto distance = center - character->getPosition();
distance /= size;
if( glm::length( distance ) < 1.f ) return true;
}
return false;
}
bool game_character_near_point_in_vehicle(const ScriptArguments& args)
{
auto character = static_cast<CharacterObject*>(args.getObject<CharacterObject>(0));
@ -432,6 +449,21 @@ void game_create_character_in_vehicle(const ScriptArguments& args)
*args[3].globalInteger = character->getGameObjectID();
}
bool game_is_vehicle_model(const ScriptArguments& args)
{
auto vdata = args.getWorld()->data->findObjectType<VehicleData>(args[1].integer);
if( vdata )
{
auto vehicle = args.getObject<VehicleObject>(0);
if ( vehicle ) {
return vehicle->model && vdata->modelName == vehicle->model->name;
}
}
return false;
}
void game_set_player_heading(const ScriptArguments& args)
{
auto object = args.getPlayerCharacter(0);
@ -1016,6 +1048,7 @@ ObjectModule::ObjectModule()
bindUnimplemented( 0x00F5, game_locate_character_in_sphere, 8, "Locate Player In Sphere" );
bindFunction(0x00F6, game_player_near_point_on_foot_3D, 8, "Is Player near point on foot" );
bindFunction(0x00F7, game_player_near_point_in_vehicle_3D, 8, "Is Player near point in car" );
bindFunction(0x0100, game_character_near_point_in_vehicle, 8, "Is Character near point in car" );
@ -1031,6 +1064,8 @@ ObjectModule::ObjectModule()
bindFunction(0x0129, game_create_character_in_vehicle, 4, "Create Character In Car" );
bindUnimplemented(0x0135, game_set_vehicle_locked, 2, "Set Vehicle locked state");
bindFunction(0x0137, game_is_vehicle_model, 2, "Is Vehicle Model" );
bindFunction(0x0171, game_set_player_heading, 2, "Set Player Heading" );

View File

@ -11,11 +11,13 @@ var app = angular.module('debugApp', []);
app.controller('DebugCtrl', function($scope,$http) {
$scope.threads = [];
$scope.running = true;
$scope.breakpoint = {};
$scope.refresh = function() {
var promise = $http.get('/state')
.success(function(data, status, headers, config) {
$scope.running = data.status == 'running';
$scope.threads = data.threads;
$scope.breakpoint = data.breakpoint;
});
};
$scope.interrupt = function() {
@ -73,10 +75,11 @@ html,body { font-family: sans-serif; }
</div>
<span id="running" style="background: green; color: white; padding: 2px; border-radius: 2px" ng-show="running">Game is running</span>
<div>
{{ breakpoint.program_counter }} {{ breakpoint.thread }}
<h2>Threads</h2>
<ul>
<li ng-repeat="thread in threads">
<h3>{{thread.name}}</h3>
<h3>{{thread.name}} ({{thread.address}})</h3>
<i>program counter: </i> {{thread.program_counter}}<br>
<i>wake counter: </i> {{thread.wake_counter}} ms<br>
<h4>Return Stack</h4>
@ -87,7 +90,8 @@ html,body { font-family: sans-serif; }
</ol>
<h4>Disassembly</h4>
<div class="disassembly">
<div ng-repeat="call in thread.disassembly" class="instruction">
<div ng-repeat="call in thread.disassembly" class="instruction"
ng-class="(breakpoint.program_counter == call.address && thread.address == breakpoint.thread)? 'breakpoint' : ''">
<span class="line-number">
{{ call.address }}
</span>
@ -117,12 +121,11 @@ html,body { font-family: sans-serif; }
</html>)";
HttpServer::HttpServer(RWGame* game, GameWorld* world)
: game(game), world(world), paused(false)
: game(game), world(world), paused(false), lastBreakpoint(nullptr)
{}
void HttpServer::run()
{
listener.create();
listener.listen(8091);
std::cout << "STARTING HTTP SERVER" << std::endl;
@ -161,6 +164,7 @@ void HttpServer::run()
void HttpServer::handleBreakpoint(const SCMBreakpoint &bp)
{
lastBreakpoint = &bp;
paused = true;
while( paused ) {
@ -237,7 +241,7 @@ std::string thread_disassembly(ScriptMachine* script, SCMThread& th)
ss << "{";
if( script->getOpcodes()->findOpcode((uint16_t)opcode, &meta) )
{
ss << "\"address\": " << it->first << ","
ss << "\"address\": \"" << it->first << "\","
<< "\"function\": \"" << meta->signature << "\","
<< "\"arguments\": [";
SCMParams& parameters = it->second.parameters;
@ -267,6 +271,7 @@ std::string thread(ScriptMachine* script, SCMThread& th)
{
std::stringstream ss;
ss << "{"
<< "\"address\": \"" << &th << "\","
<< "\"program_counter\": " << th.programCounter << ","
<< "\"name\": \"" << th.name << "\","
<< "\"wake_counter\": " << th.wakeCounter << ","
@ -276,6 +281,20 @@ std::string thread(ScriptMachine* script, SCMThread& th)
return ss.str();
}
std::string breakpoint(const SCMBreakpoint* breakpoint)
{
std::stringstream ss;
ss << "{";
if( breakpoint != nullptr )
{
ss << "\"program_counter\": " << breakpoint->pc << ","
<< "\"thread\": \"" << breakpoint->thread << "\"";
}
ss << "}";
return ss.str();
}
std::string HttpServer::getState() const
{
if( !paused ) {
@ -285,12 +304,14 @@ std::string HttpServer::getState() const
std::stringstream ss;
ss << "{";
ss << R"("status":"interrupted",)";
ss << R"("breakpoint": )" << breakpoint(lastBreakpoint) << ",";
ss << R"("threads": [)";
for(unsigned int i = 0; i < game->getScript()->getThreads().size(); ++i)
{
SCMThread& th = game->getScript()->getThreads()[i];
bool last = (game->getScript()->getThreads().size() == i+1);
ss << thread(game->getScript(), th) << (last ? "" : ",");
if( i != 0 )
ss << ",";
SCMThread& th = game->getScript()->getThreads().at(i);
ss << thread(game->getScript(), th);
}
ss << R"(])";
ss << "}";
@ -320,12 +341,14 @@ std::string HttpServer::dispatch(std::string method, std::string path)
else if(path == "/step") {
if( paused ) {
game->getScript()->interuptNext();
lastBreakpoint = nullptr;
paused = false;
}
ss << getState();
mime = "application/json";
}
else if(path == "/continue") {
lastBreakpoint = nullptr;
paused = false;
ss << getState();
mime = "application/json";

View File

@ -10,20 +10,6 @@
#include <sys/socket.h>
#endif
class ReuseableListener : public sf::TcpListener
{
public:
void create() {
char reuse = 1;
sf::SocketHandle handle = socket(PF_INET, SOCK_STREAM, 0);
if( setsockopt(handle, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0 )
{
std::cerr << "Failed to set socket SO_REUSEADDR: errno = " << errno << std::endl;
}
Socket::create(handle);
}
};
class HttpServer
{
public:
@ -32,10 +18,11 @@ public:
void handleBreakpoint(const SCMBreakpoint& bp);
private:
ReuseableListener listener;
sf::TcpListener listener;
RWGame* game;
GameWorld* world;
bool paused;
const SCMBreakpoint* lastBreakpoint;
std::string dispatch(std::string method, std::string path);
std::string getState() const;