зеркало из https://github.com/mozilla/pjs.git
494 строки
10 KiB
C++
494 строки
10 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.0 (the "NPL"); you may not use this file except in
|
|
* compliance with the NPL. You may obtain a copy of the NPL at
|
|
* http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
|
* for the specific language governing rights and limitations under the
|
|
* NPL.
|
|
*
|
|
* The Initial Developer of this code under the NPL is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
|
* Reserved.
|
|
*/
|
|
// DebuggerChannel.cpp
|
|
//
|
|
// Scott M. Silver
|
|
|
|
#include "DebuggerChannel.h"
|
|
#include "prio.h"
|
|
#include "prthread.h"
|
|
#include "prlock.h"
|
|
#ifdef assert
|
|
#undef assert
|
|
#endif
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
|
|
void DebuggerStream::
|
|
writeRequest(Int32 inRequest)
|
|
{
|
|
DebuggerMessageRequest msgRequest = { inRequest };
|
|
|
|
writeDataRaw(&msgRequest, sizeof(msgRequest));
|
|
}
|
|
|
|
|
|
Int32 DebuggerStream::
|
|
readRequest()
|
|
{
|
|
DebuggerMessageRequest msgRequest;
|
|
|
|
readDataRaw(&msgRequest, sizeof(msgRequest));
|
|
|
|
return (msgRequest.request);
|
|
}
|
|
|
|
|
|
void DebuggerStream::
|
|
writeResponse(Int32 inStatus)
|
|
{
|
|
DebuggerMessageResponse msgResponse = { inStatus };
|
|
|
|
writeDataRaw(&msgResponse, sizeof(msgResponse));
|
|
}
|
|
|
|
|
|
Int32 DebuggerStream::
|
|
readResponse()
|
|
{
|
|
DebuggerMessageResponse msgResponse;
|
|
|
|
readDataRaw(&msgResponse, sizeof(msgResponse));
|
|
|
|
return (msgResponse.status);
|
|
}
|
|
|
|
|
|
void DebuggerStream::
|
|
writeString(const char* inString)
|
|
{
|
|
writeData(inString, strlen(inString));
|
|
}
|
|
|
|
|
|
void DebuggerStream::
|
|
writeDataRaw(const void* inData, Int32 inLength)
|
|
{
|
|
if (PR_Write(mFileDesc, inData, inLength) != inLength)
|
|
trespass("comm channel dead");
|
|
}
|
|
|
|
|
|
void DebuggerStream::
|
|
writeData(const void* inData, Int32 inLength)
|
|
{
|
|
if (PR_Write(mFileDesc, &inLength, 4) != 4)
|
|
trespass("comm channel dead");
|
|
|
|
if (PR_Write(mFileDesc, inData, inLength) != inLength)
|
|
trespass("comm channel dead");
|
|
}
|
|
|
|
|
|
Int32 DebuggerStream::
|
|
readLength()
|
|
{
|
|
Int32 length;
|
|
|
|
readDataRaw(&length, 4);
|
|
return length;
|
|
}
|
|
|
|
|
|
void DebuggerStream::
|
|
readDataRaw(void* outData, Int32 inLength)
|
|
{
|
|
if (PR_Read(mFileDesc, outData, inLength) != inLength)
|
|
trespass("comm channel dead");
|
|
}
|
|
|
|
|
|
void* DebuggerStream::
|
|
readData()
|
|
{
|
|
Int32 len = readLength();
|
|
void* data = (void*) new char[len];
|
|
|
|
readDataRaw(data, len);
|
|
|
|
return (data);
|
|
}
|
|
|
|
|
|
void DebuggerStream::
|
|
writePtr(const void* inPtr)
|
|
{
|
|
char* buffer = PR_smprintf("%p", inPtr);
|
|
writeString(buffer);
|
|
PR_smprintf_free(buffer);
|
|
}
|
|
|
|
|
|
static PRPtrdiff
|
|
convertAsciiToHex(const char* inString)
|
|
{
|
|
const char* lc = inString+strlen(inString); // point to '\0'
|
|
PRPtrdiff value = 0;
|
|
PRIntn multiplier = 0;
|
|
|
|
while (--lc >= inString)
|
|
{
|
|
if (isdigit(*lc))
|
|
value += ((*lc - 48) << multiplier);
|
|
else if ((*lc >= 97) && (*lc <= 102))
|
|
value += ((*lc - 87) << multiplier);
|
|
else
|
|
return (value); // bail
|
|
|
|
multiplier += 4;
|
|
}
|
|
|
|
return (value);
|
|
}
|
|
|
|
|
|
char* DebuggerStream::
|
|
readString()
|
|
{
|
|
Int32 len = readLength();
|
|
char* data = new char[len+1];
|
|
|
|
readDataRaw(data, len);
|
|
data[len+1-1] = '\0'; // null terminate string
|
|
|
|
return (data);
|
|
}
|
|
|
|
|
|
void* DebuggerStream::
|
|
readPtr()
|
|
{
|
|
char* data = readString();
|
|
void* rv;
|
|
|
|
rv = (void*) convertAsciiToHex(data);
|
|
|
|
delete[] data;
|
|
|
|
return (rv);
|
|
}
|
|
|
|
bool DebuggerClientChannel::
|
|
waitForAsyncRequest()
|
|
{
|
|
for (;;)
|
|
{
|
|
Int32 request = mAsync.readRequest();
|
|
|
|
|
|
handleAsyncRequest(request);
|
|
}
|
|
|
|
return (true);
|
|
}
|
|
|
|
void DebuggerClientChannel::
|
|
asyncRequestThread(void* inThisPtr)
|
|
{
|
|
DebuggerClientChannel* thisChannel = (DebuggerClientChannel*) inThisPtr;
|
|
|
|
thisChannel->waitForAsyncRequest();
|
|
}
|
|
|
|
DebuggerClientChannel* DebuggerClientChannel::
|
|
createClient()
|
|
{
|
|
PRFileDesc* async;
|
|
PRFileDesc* sync;
|
|
|
|
// specify loopback
|
|
PRNetAddr netaddr;
|
|
|
|
netaddr.inet.family = AF_INET;
|
|
netaddr.inet.port = htons(kDebuggerPort);
|
|
netaddr.inet.ip = htonl(0x7f000001); // 127.0.0.1
|
|
|
|
if ((sync = PR_NewTCPSocket()) == NULL)
|
|
return (0);
|
|
|
|
if (PR_Connect(sync, &netaddr, PR_INTERVAL_NO_TIMEOUT) < 0)
|
|
return (0);
|
|
|
|
if ((async = PR_NewTCPSocket()) == NULL)
|
|
return (0);
|
|
|
|
if (PR_Connect(async, &netaddr, PR_INTERVAL_NO_TIMEOUT) < 0)
|
|
return (0);
|
|
|
|
DebuggerClientChannel* channel = new DebuggerClientChannel(sync, async);
|
|
PRThread* thread;
|
|
|
|
thread = PR_CreateThread(PR_USER_THREAD,
|
|
&asyncRequestThread,
|
|
channel,
|
|
PR_PRIORITY_NORMAL,
|
|
PR_GLOBAL_THREAD,
|
|
PR_JOINABLE_THREAD,
|
|
0);
|
|
|
|
// if we can't create the async thread, this is failure
|
|
if (!thread)
|
|
{
|
|
delete channel;
|
|
return (0);
|
|
}
|
|
|
|
return (channel);
|
|
}
|
|
|
|
// Constructor locks the passed in lock
|
|
// Destructor unlocks the lock.
|
|
// Useful for a one line critical section
|
|
class StLocker
|
|
{
|
|
PRLock* mLock;
|
|
|
|
public:
|
|
StLocker(PRLock* inLock) :
|
|
mLock(inLock) { PR_Lock(mLock); }
|
|
|
|
~StLocker()
|
|
{
|
|
PRStatus status = PR_Unlock(mLock);
|
|
assert(status == PR_SUCCESS);
|
|
}
|
|
};
|
|
|
|
|
|
char* DebuggerClientChannel::
|
|
requestAddressToMethod(const void* inAddress, Int32& outOffset)
|
|
{
|
|
StLocker locker(mLock);
|
|
mSync.writeRequest(kAddressToMethod);
|
|
|
|
// now send the arguments (one hex address)
|
|
mSync.writePtr(inAddress);
|
|
|
|
Int32 response = mSync.readResponse();
|
|
|
|
if (!response)
|
|
{
|
|
char *methodName = mSync.readString();
|
|
outOffset = (Int32) mSync.readPtr();
|
|
return (methodName);
|
|
}
|
|
|
|
return (NULL);
|
|
}
|
|
|
|
DebuggerClientChannel::
|
|
DebuggerClientChannel(PRFileDesc* inSync, PRFileDesc* inAsync) :
|
|
mSync(inSync),
|
|
mAsync(inAsync),
|
|
mCompLoadHandler(0)
|
|
{
|
|
mLock = PR_NewLock();
|
|
assert(mLock);
|
|
}
|
|
|
|
void* DebuggerClientChannel::
|
|
requestMethodToAddress(const char* inMethodName)
|
|
{
|
|
StLocker locker(mLock);
|
|
mSync.writeRequest(kMethodToAddress);
|
|
|
|
// now send the arguments (one hex address)
|
|
mSync.writeString(inMethodName);
|
|
|
|
Int32 response = mSync.readResponse();
|
|
|
|
void* address = (response == 0) ? mSync.readPtr() : 0;
|
|
|
|
return (address);
|
|
}
|
|
|
|
void DebuggerClientChannel::
|
|
sendOneCharacterRequest(char inCharRequest)
|
|
{
|
|
StLocker locker(mLock);
|
|
mSync.writeRequest((Int32) inCharRequest);
|
|
}
|
|
|
|
void DebuggerClientChannel::
|
|
handleAsyncRequest(Int32 inRequest)
|
|
{
|
|
// method does not get run until
|
|
if (inRequest == kNotifyMethodCompiledLoaded)
|
|
{
|
|
char* methodName = mAsync.readString();
|
|
void* address = mAsync.readPtr();
|
|
|
|
handleCompileOrLoadedMethod(methodName, address);
|
|
delete [] methodName;
|
|
mAsync.writeResponse(0);
|
|
}
|
|
else
|
|
mAsync.writeResponse(-1);
|
|
}
|
|
|
|
|
|
Int32 DebuggerClientChannel::
|
|
requestNotifyOnMethodCompileLoad(const char* inMethodName)
|
|
{
|
|
StLocker locker(mLock);
|
|
mSync.writeRequest(kRequestNotifyMethodCompiledLoaded);
|
|
|
|
// now send the arguments (one string)
|
|
mSync.writeString(inMethodName);
|
|
|
|
return(mSync.readResponse());
|
|
}
|
|
|
|
Int32 DebuggerClientChannel::
|
|
requestRunClass(const char* inClassName)
|
|
{
|
|
StLocker locker(mLock);
|
|
mSync.writeRequest(kRunClass);
|
|
|
|
// now send the arguments (one string)
|
|
mSync.writeString(inClassName);
|
|
|
|
return (mSync.readResponse());
|
|
}
|
|
|
|
Int32 DebuggerClientChannel::
|
|
requestNotifyOnClassLoad(const char* inClassName)
|
|
{
|
|
StLocker locker(mLock);
|
|
mSync.writeRequest(kNotifyOnClassLoad);
|
|
|
|
// now send the arguments (one string)
|
|
mSync.writeString(inClassName);
|
|
|
|
return(mSync.readResponse());
|
|
}
|
|
|
|
Int32 DebuggerClientChannel::
|
|
requestNotifyOnException(const char* inClassName)
|
|
{
|
|
StLocker locker(mLock);
|
|
mSync.writeRequest(kNotifyOnException);
|
|
|
|
// now send the arguments (one string)
|
|
mSync.writeString(inClassName);
|
|
|
|
return(mSync.readResponse());
|
|
}
|
|
|
|
void* DebuggerClientChannel::
|
|
requestDebuggerThread()
|
|
{
|
|
StLocker locker(mLock);
|
|
mSync.writeRequest(kRequestDebuggerThread);
|
|
|
|
if (!mSync.readResponse())
|
|
return (mSync.readPtr());
|
|
else
|
|
return (0);
|
|
}
|
|
|
|
#ifdef WIN32
|
|
// The DLL entry-point function sets up shared memory using
|
|
// a named file-mapping object.
|
|
|
|
#include <windows.h>
|
|
#include <memory.h>
|
|
|
|
static DWORD* sThreadID = NULL; // pointer to shared memory
|
|
|
|
BOOL WINAPI DllEntryPoint(HINSTANCE hinstDLL, // DLL module handle
|
|
DWORD fdwReason, // reason called
|
|
LPVOID lpvReserved) // reserved
|
|
{
|
|
HANDLE hMapObject = NULL; // handle to file mapping
|
|
BOOL fInit, fIgnore;
|
|
|
|
switch (fdwReason)
|
|
{
|
|
// The DLL is loading due to process
|
|
// initialization or a call to LoadLibrary.
|
|
|
|
case DLL_PROCESS_ATTACH:
|
|
// Create a named file mapping object.
|
|
hMapObject = CreateFileMapping(
|
|
(HANDLE) 0xFFFFFFFF, // use paging file
|
|
NULL, // no security attributes
|
|
PAGE_READWRITE, // read/write access
|
|
0, // size: high 32-bits
|
|
sizeof(*sThreadID), // size: low 32-bits
|
|
"debuggerThreadID"); // name of map object
|
|
if (hMapObject == NULL)
|
|
return FALSE;
|
|
|
|
// The first process to attach initializes memory.
|
|
fInit = (GetLastError() != ERROR_ALREADY_EXISTS);
|
|
|
|
// Get a pointer to the file-mapped shared memory.
|
|
sThreadID = (DWORD*) MapViewOfFile(
|
|
hMapObject, // object to map view of
|
|
FILE_MAP_WRITE, // read/write access
|
|
0, // high offset: map from
|
|
0, // low offset: beginning
|
|
0); // default: map entire file
|
|
if (sThreadID == NULL)
|
|
return FALSE;
|
|
|
|
// Initialize memory if this is the first process.
|
|
if (fInit)
|
|
memset(sThreadID, 0, sizeof(*sThreadID));
|
|
|
|
break;
|
|
// The attached process creates a new thread.
|
|
case DLL_THREAD_ATTACH:
|
|
break;
|
|
// The thread of the attached process terminates.
|
|
case DLL_THREAD_DETACH:
|
|
break;
|
|
// The DLL is unloading from a process due to
|
|
// process termination or a call to FreeLibrary.
|
|
case DLL_PROCESS_DETACH:
|
|
// Unmap shared memory from the process's address space.
|
|
fIgnore = UnmapViewOfFile(sThreadID);
|
|
|
|
// Close the process's handle to the file-mapping object.
|
|
|
|
fIgnore = CloseHandle(hMapObject);
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
UNREFERENCED_PARAMETER(hinstDLL);
|
|
UNREFERENCED_PARAMETER(lpvReserved);
|
|
}
|
|
|
|
void setDebuggerThreadID(DWORD inThreadID)
|
|
{
|
|
*sThreadID = inThreadID;
|
|
}
|
|
|
|
DWORD getDebuggerThreadID()
|
|
{
|
|
return (*sThreadID);
|
|
}
|
|
#endif
|