[interfacekit] Re: A patch for BString
- From: Ingo Weinhold <bonefish@xxxxxxxxxxxxxxx>
- To: interfacekit@xxxxxxxxxxxxx
- Date: Sat, 08 Nov 2003 02:28:09 +0100
On 2003-11-08 at 00:05:21 [+0100], you wrote:
>
> On 2003-11-06 at 21:36:18 [+0100], Ingo Weinhold wrote:
> >
> > A better solution seems to redirect the debugger() invocation. By
> > accident,
> > not too long ago, I wrote some code that allows to redirect calls to
> > functions that live in a different shared object at least. Since
> > debugger()
> > is defined in libroot, that should be just fine for invocations from
> > libbe/libopenbeos.
> >
> > If noone objects, I can try and extend our unit test framework to support
> > testing for debugger calls.
>
> Out of curiosity, I have fiddled a bit with the stuff in <debugger.h> and it
> seems that you can install a debugger inside your own team, all you need is
> a
> new thread.
Ah cool, I suspected, all threads of the team would be put on hold, when the
debugger is entered. I should have known better...
> Enclosed you find a proof-of-concept.
>
> There is one problem, though: if the application crashes (e.g. seg-faults)
> the dummy-debugger gets invoked, too.
> My implementation in that case notices that is has been called for the wrong
> reason and tries to fall back to the default debugger, but that doesn't seem
> to work from inside the debugger: nothing happens.
What works, is to remove the debugger and then call debugger(). Then one
doesn't know, what the original problem was, but it should be possible to
find out once being in the real debugger.
I adjusted the sources a bit -- a few small fixes and the forementioned
change. Moreover I tried to catch signals and deinstall the debugger when a
signal is caught, i.e. when the real debugger should be entered and that
works fine, if a signal occurs before the debugger is hit the first time (but
it's not thread safe as it is now). But once the debugger was invoked, it
seems that removing it won't reinstall the default debugger. The thread hangs
waiting on the nub port.
> So we should probably go with Ingo's solution as it seems more reliable, but
> it may be worthwile to invest some more time into the debugger-stuff.
I actually like the debugger solution better, for it's less a hack. And one
can probably live with not hitting the real debugger with the right thread
and the original cause.
CU, Ingo
#include <cstring>
#include <iostream>
#include <signal.h>
#include <assert.h>
#include <debugger.h>
#include <Debug.h>
#include <List.h>
#include <Locker.h>
#define ASSERT_DEBUGGER_CALL( expr) \
{ \
thread_id tid = find_thread( NULL); \
testDebugger.ClearThreadHasCalledDebugger( tid); \
expr; \
if (!testDebugger.ThreadHasCalledDebugger( tid)) \
cerr << "assertion failed" << endl; \
}
struct TestTeamDebugger {
TestTeamDebugger();
~TestTeamDebugger();
status_t InitCheck() const { return
fInitCheck; }
bool ThreadHasCalledDebugger( thread_id tid);
void SetThreadHasCalledDebugger( thread_id tid);
void ClearThreadHasCalledDebugger( thread_id tid);
void TerminateDebugger();
private:
static int32 StartDebuggerThread( void* data);
void InstallDebugger();
void RemoveDebugger();
int32 RunDebugger();
status_t fInitCheck;
port_id fDebugPort;
port_id fOriginalDebugPort;
thread_id fDebugThread;
team_id fMyTeam;
BLocker fThreadDataLocker;
BList fThreadData;
bool fShutdown;
enum {
SYNC_DEBUGGER = 'sDbg'
};
};
TestTeamDebugger::TestTeamDebugger()
: fInitCheck( B_NO_INIT)
, fDebugPort( -1)
, fDebugThread( -1)
, fMyTeam( -1)
, fThreadDataLocker( "TestTeamDebugger")
, fShutdown( false)
{
fDebugThread = spawn_thread( StartDebuggerThread,
"TestTeamDebugThread",
B_NORMAL_PRIORITY, (void*)this);
if (fDebugThread < 0) {
fInitCheck = fDebugThread;
return;
}
if ((fInitCheck = send_data( fDebugThread, SYNC_DEBUGGER, NULL, 0)) < 0)
return;
if ((fInitCheck = resume_thread( fDebugThread)) < 0)
return;
thread_id sender;
if ((fInitCheck = receive_data( &sender, NULL, 0)) != SYNC_DEBUGGER)
return;
}
TestTeamDebugger::~TestTeamDebugger() {
TerminateDebugger();
}
void TestTeamDebugger::TerminateDebugger() {
fShutdown = true;
if (fDebugThread >= 0) {
status_t result;
wait_for_thread( fDebugThread, &result);
fDebugThread = -1;
}
}
bool TestTeamDebugger::ThreadHasCalledDebugger( thread_id tid) {
fThreadDataLocker.Lock();
bool result = fThreadData.HasItem( (void*)tid);
fThreadDataLocker.Unlock();
return result;
}
void TestTeamDebugger::SetThreadHasCalledDebugger( thread_id tid) {
fThreadDataLocker.Lock();
if (!fThreadData.HasItem( (void*)tid))
fThreadData.AddItem( (void*)tid);
fThreadDataLocker.Unlock();
}
void TestTeamDebugger::ClearThreadHasCalledDebugger( thread_id tid) {
fThreadDataLocker.Lock();
fThreadData.RemoveItem( (void*)tid);
fThreadDataLocker.Unlock();
}
int32 TestTeamDebugger::StartDebuggerThread( void* data) {
status_t err;
TestTeamDebugger* dbg = reinterpret_cast<TestTeamDebugger*>( data);
thread_id sender;
if ((dbg->fInitCheck = receive_data( &sender, NULL, 0)) !=
SYNC_DEBUGGER)
return dbg->fInitCheck;
dbg->InstallDebugger();
if ((dbg->fInitCheck = send_data( sender, SYNC_DEBUGGER, NULL, 0)) < 0)
return dbg->fInitCheck;
if (dbg->fInitCheck == B_OK)
dbg->RunDebugger();
dbg->RemoveDebugger();
return dbg->fInitCheck;
}
void TestTeamDebugger::InstallDebugger() {
if ((fDebugPort = create_port( 10, "TestTeamDebuggerPort")) < 0) {
fInitCheck = fDebugPort;
return;
}
thread_info tInfo;
if ((fInitCheck = get_thread_info( find_thread(NULL), &tInfo)) < 0)
return;
fMyTeam = tInfo.team;
status_t err = install_team_debugger( fMyTeam, fDebugPort);
if (err < 0)
fInitCheck = err;
}
void TestTeamDebugger::RemoveDebugger() {
if (fMyTeam >= 0) {
fInitCheck = remove_team_debugger( fMyTeam);
printf("remove_team_debugger(): %ld\n", fInitCheck);
fMyTeam = -1;
}
if (fDebugPort >= 0) {
fInitCheck = close_port( fDebugPort);
fDebugPort = -1;
}
}
int32 TestTeamDebugger::RunDebugger() {
status_t err;
size_t bufSize;
int32 msgCode;
while( !fShutdown) {
do {
bufSize = port_buffer_size_etc( fDebugPort, B_TIMEOUT,
100*1000);
if (bufSize < 0 && bufSize != B_TIMED_OUT)
return bufSize;
if (fShutdown)
return B_OK;
} while (bufSize == B_TIMED_OUT);
char msgBuf[bufSize];
ssize_t readLen = read_port( fDebugPort, &msgCode, &msgBuf,
bufSize);
if (readLen < 0)
return readLen;
printf("debugger msg: %ld\n", msgCode);
switch (msgCode) {
case B_THREAD_STOPPED: {
// the system tells us that it has stopped a
thread,
// first we check why...
db_thread_stopped_msg* stoppedInfo =
(db_thread_stopped_msg*)msgBuf;
if (stoppedInfo->why == B_DEBUGGER_CALL) {
// correct reason for test-debugger, so
we take notice
// which thread has called for a
debug-session:
SetThreadHasCalledDebugger(
stoppedInfo->thread);
// tell the nub to let the thread
continue:
nub_run_thread_msg runInfo;
runInfo.thread = stoppedInfo->thread;
runInfo.cpu = stoppedInfo->cpu;
if ((err = write_port(
stoppedInfo->nub_port, B_RUN_THREAD,
(void*)&runInfo, sizeof(runInfo))) < 0) {
cerr << "TestTeamDebugger:
error in write_port: "
<< strerror( err) <<
endl;
return err;
}
} else {
// ugh, wrong reason, we don't want to
handle this (real) error,
// so we remove ourselves and fallback
to the real debugger:
cerr << "TestTeamDebugger: called for
unexpected reason "
<< stoppedInfo->why << ",
calling real debugger!" << endl;
RemoveDebugger();
fShutdown = true;
debugger("Something terrible
happened!");
}
break;
}
default:
cerr << "TestTeamDebugger: read unexpected code
" << msgCode
<< "from nub-port, it's ignored!" <<
endl;
};
}
return B_OK;
}
void
signal_handler_function(int signal, void *cookie, struct vregs *registers)
{
printf("caught signal: %d\n", signal);
TestTeamDebugger *debugger = (TestTeamDebugger *)cookie;
debugger->TerminateDebugger();
printf("deinstalled debugger\n");
for (int i = 1; i <= 32; i++)
::signal(i, NULL);
printf("removed signal handler\n");
}
int main() {
TestTeamDebugger testDebugger;
status_t err = testDebugger.InitCheck();
if (err < 0) {
cerr << "Creation of TestTeamDebugger failed: " << strerror(
err) << endl;
exit( 5);
}
struct sigaction signal_handler = {
(__signal_func_ptr)signal_handler_function,
0, // sa_mask
0, // sa_flags
&testDebugger, // sa_userdata
};
for (int i = 1; i <= 32; i++)
sigaction(i, &signal_handler, NULL);
// works fine here
//int *bad = NULL;
//*bad = 0;
ASSERT_DEBUGGER_CALL( debugger("Hello debugger!"));
cout << "back from the living dead!" << endl;
// but fails (hangs without entering the real debugger) here
//int *bad = NULL;
//*bad = 0;
ASSERT_DEBUGGER_CALL( cout << "should fail..." << endl);
}
- Follow-Ups:
- [interfacekit] Re: A patch for BString
- From: Ingo Weinhold
- References:
- [interfacekit] A patch for BString
- From: Oliver Tappe
- [interfacekit] Re: A patch for BString
- From: Ingo Weinhold
- [interfacekit] Re: A patch for BString
- From: Oliver Tappe
Other related posts:
- » [interfacekit] A patch for BString
- » [interfacekit] Re: A patch for BString
- » [interfacekit] Re: A patch for BString
- » [interfacekit] Re: A patch for BString
- » [interfacekit] Re: A patch for BString
- » [interfacekit] Re: A patch for BString
- » [interfacekit] Re: A patch for BString
- » [interfacekit] Re: A patch for BString
- » [interfacekit] Re: A patch for BString
- » [interfacekit] Re: A patch for BString
- » [interfacekit] Re: A patch for BString
- » [interfacekit] Re: A patch for BString
- » [interfacekit] Re: A patch for BString
- » [interfacekit] Re: A patch for BString
- » [interfacekit] Re: A patch for BString
- » [interfacekit] Re: A patch for BString
- » [interfacekit] Re: A patch for BString
- » [interfacekit] Re: A patch for BString
- » [interfacekit] Re: A patch for BString
- » [interfacekit] Re: A patch for BString
- » [interfacekit] Re: A patch for BString
- » [interfacekit] Re: A patch for BString
- » [interfacekit] Re: A patch for BString
- [interfacekit] Re: A patch for BString
- From: Ingo Weinhold
- [interfacekit] A patch for BString
- From: Oliver Tappe
- [interfacekit] Re: A patch for BString
- From: Ingo Weinhold
- [interfacekit] Re: A patch for BString
- From: Oliver Tappe