gecko-dev/ef/Debugger/Debugger.cpp

344 строки
7.3 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*/
#include <stdio.h>
#include "CatchAssert.h"
#include "Debugger.h"
#ifdef USE_JVMDI
#include "jvmdi.h"
#endif
#include "LogModule.h"
#include "NativeCodeCache.h"
#include "DebuggerChannel.h"
#include "StringUtils.h"
#include "JavaVM.h"
UT_DEFINE_LOG_MODULE(Debugger);
DebuggerState::DebuggerState(Pool& p) :
breakpoints(NULL),
enabled(false),
pool(p)
{
}
DebuggerState::DebuggerState(Pool& p, bool _enabled) : pool(p)
{
enabled = _enabled;
if (enabled)
enable();
}
void
DebuggerState::enable()
{
enabled = true;
breakpoints = new (pool) BreakPoint[MAX_BREAKPOINTS];
thread = DebuggerServerChannel::spawnServer();
#ifdef LINUX
// Set the breakpoint handler up.
SetupBreakPointHandler();
#endif
}
void
DebuggerState::waitForDebugger()
{
if (thread)
PR_JoinThread(thread);
}
static PRNetAddr sNetaddr;
PRThread* DebuggerServerChannel::
spawnServer()
{
PRFileDesc* connector;
// Create a TCP socket
if ((connector = PR_NewTCPSocket()) == NULL)
{
UT_LOG(Debugger, PR_LOG_DEBUG, ("Debugger: PR_NewTCPSocket failed\n"));
return (NULL);
}
sNetaddr.inet.family = AF_INET;
sNetaddr.inet.port = htons(kDebuggerPort);
sNetaddr.inet.ip = htonl(INADDR_ANY);
if (PR_Bind(connector, &sNetaddr) < 0)
{
UT_LOG(Debugger, PR_LOG_DEBUG, ("Debugger: PR_Bind failed\n"));
return (NULL);
}
// Allow atmost 2 debugger connections
// 1 sync 1 async
if (PR_Listen(connector, 2) < 0)
{
UT_LOG(Debugger, PR_LOG_DEBUG, ("Debugger: PR_Listen failed\n"));
return (NULL);
}
PRThread* thread;
// Spawn the debugger listener thread
thread = PR_CreateThread(PR_USER_THREAD,
&serverThread,
connector,
PR_PRIORITY_NORMAL,
PR_GLOBAL_THREAD,
PR_JOINABLE_THREAD,
0);
return (thread);
}
void DebuggerServerChannel::
serverThread(void* inArgument)
{
#ifdef _WIN32
SET_DEBUGGER_THREAD_ID(::GetCurrentThreadId());
#endif
PRFileDesc* connector = (PRFileDesc*) inArgument;
PRFileDesc* sync;
PRFileDesc* async;
if (!(sync = PR_Accept(connector, &sNetaddr, PR_INTERVAL_NO_TIMEOUT)))
assert(false);
if (!(async = PR_Accept(connector, &sNetaddr, PR_INTERVAL_NO_TIMEOUT)))
assert(false);
DebuggerServerChannel serverChannel(sync, async);
serverChannel.serverLoop();
}
void
DebuggerState:: clearAllBreakPoints()
{
for (int i=0; i < MAX_BREAKPOINTS; i++)
breakpoints[i].clear();
}
void DebuggerServerChannel::
serverLoop()
{
#define HELL_HAS_NOT_FROZEN_OVER 1
while (HELL_HAS_NOT_FROZEN_OVER)
{
Int32 request;
request = mSync.readRequest();
UT_LOG(Debugger, PR_LOG_DEBUG, ("command received: %c\n", (char) request));
switch(request)
{
case 'b':
#ifdef USE_JVMDI
JVMDI_SetBreakpoint(NULL, 0, 0, 0);
#endif
break;
case 'g':
// Not implemented
break;
case kAddressToMethod:
// request address to method
handleAddressToMethod();
break;
case kMethodToAddress:
// request method to address
handleMethodToAddress();
break;
case kRequestNotifyMethodCompiledLoaded:
handleNotifyOnCompileLoadMethod();
break;
case kRunClass:
handleRunClass();
break;
case 'q':
// Debugger thread quits
return;
default:
mSync.writeResponse(-1);
break;
}
}
}
void
DebuggerState::setBreakPoint(void *pc)
{
#ifdef LINUX
SetBreakPoint(pc);
#else
pc;
#endif
}
void
DebuggerState::clearBreakPoint(void *pc)
{
clearBreakPoint(pc);
}
// Access to the database of classes
#include "ClassWorld.h"
#include "StringPool.h"
extern ClassWorld world;
extern StringPool sp;
void DebuggerServerChannel::
handleAddressToMethod()
{
DEBUG_TRACER(UT_LOG_MODULE(Debugger), "handleAddressToMethod");
void* address;
CacheEntry* ce;
address = mSync.readPtr();
ce = NativeCodeCache::getCache().lookupByRange((Uint8*) address);
if (ce) {
mSync.writeResponse(0);
mSync.writeString(ce->descriptor.method->toString());
mSync.writePtr((void*)((Uint8*) address - ce->start.getFunctionAddress()));
}
else
mSync.writeResponse(-1);
}
void DebuggerServerChannel::
handleMethodToAddress()
{
DEBUG_TRACER(UT_LOG_MODULE(Debugger), "handleMethodToAddress");
char* methodName;
const char* internedMethodName;
// lookup method name in string pool (getMethod requires interned string)
internedMethodName = sp.get(methodName = mSync.readString());
delete [] methodName;
if (internedMethodName) {
Method* method;
// now grab the method
if (world.getMethod(internedMethodName, method)) {
// lookup method in cache
MethodDescriptor methodDescriptor(*method);
void* address = addressFunction(NativeCodeCache::getCache().lookupByDescriptor(methodDescriptor, true, true));
if (address) {
mSync.writeResponse(0);
mSync.writePtr(address);
return;
}
}
}
// if we fall-through the method was not found
mSync.writeResponse(-1);
}
Vector<char*> sNotifyList;
void DebuggerServerChannel::
handleNotifyOnCompileLoadMethod()
{
DEBUG_TRACER(UT_LOG_MODULE(Debugger), "handleNotifyOnCompileLoadMethod");
char* methodName = mSync.readString();
sNotifyList.append(methodName); // methodName is new'ed for us by DebuggerChannel
mSync.writeResponse(0);
}
void executeClass(void *inArgument);
void executeClass(void *inArgument)
{
char* className = (char*) inArgument;
VM::theVM.execute(className, NULL, NULL, NULL, 0);
}
void DebuggerServerChannel::
handleRunClass()
{
DEBUG_TRACER(UT_LOG_MODULE(Debugger), "handleRunClass");
char* className = mSync.readString();
PRThread* thread;
// do not tie up this thread -- because we
// we need this one for communication
thread = PR_CreateThread(PR_USER_THREAD,
&executeClass,
className,
PR_PRIORITY_NORMAL,
PR_GLOBAL_THREAD,
PR_JOINABLE_THREAD,
0);
mSync.writeResponse(thread ? 0 : -1); // return -1 if couldn't create thread
// FIX-ME leak className because too lazy
// to make sure thread makes its own copy
//delete [] className;
}
void DebuggerServerChannel::
listenToMessage(BroadcastEventKind inKind, void* inArgument)
{
if (inKind == kEndCompileOrLoad) {
assert(inArgument);
Method& method = *(Method*) inArgument;
char** curPos;
for (curPos = sNotifyList.begin(); curPos < sNotifyList.end(); curPos++)
if (!PL_strcmp(*curPos, method.toString())) {
mAsync.writeRequest(kNotifyMethodCompiledLoaded);
mAsync.writeString(*curPos);
MethodDescriptor methodDescriptor(method);
void* address = addressFunction(NativeCodeCache::getCache().lookupByDescriptor(methodDescriptor, true, true));
assert(address);
mAsync.writePtr(address);
// now wait for a response
assert(!mAsync.readResponse());
break;
}
}
}