status_t thread_debug_thread(void *arg) { Tdebug_thead_param* param = (Tdebug_thead_param*) arg; debug_thread(param->thread); delete param; return B_OK; }
static status_t haiku_stop_thread(team_debug_info *teamDebugInfo, thread_id threadID) { // check whether we know the thread status_t err; thread_event_closure threadEventClosure; thread_debug_info *threadInfo = haiku_find_thread(teamDebugInfo, threadID); if (!threadInfo) return B_BAD_THREAD_ID; // already stopped? if (threadInfo->stopped) return B_OK; // debug the thread err = debug_thread(threadID); if (err != B_OK) { TRACE(("haiku_stop_thread(): failed to debug thread %ld: %s\n", threadID, strerror(err))); return err; } // wait for the event to hit our port // TODO: debug_thread() doesn't guarantee that the thread will enter the // debug loop any time soon. E.g. a wait_for_thread() is (at the moment) // not interruptable, so we block here forever, if we already debug the // thread that is being waited for. We should probably limit the time // we're waiting, and return the info to the caller, which can then try // to deal with the situation in an appropriate way. // prepare closure for finding interesting events threadEventClosure.context = teamDebugInfo; threadEventClosure.thread = threadID; threadEventClosure.event = NULL; // find the first interesting queued event threadEventClosure.event = haiku_find_next_debug_event( &teamDebugInfo->events, haiku_thread_event_predicate, &threadEventClosure); // read all events pending on the port haiku_read_pending_debug_events(teamDebugInfo, (threadEventClosure.event == NULL), haiku_thread_event_predicate, &threadEventClosure); // We should check, whether we really got an event for the thread in // question, but the only possible other event is that the team has // been delete, which ends the game anyway. // TODO: That's actually not true. We also get messages when an add-on // has been loaded/unloaded, a signal arrives, or a thread calls the // debugger. return B_OK; }
static void haiku_child_stop_inferior (void) { status_t err; thread_id threadID = ptid_get_tid(inferior_ptid); TRACE(("haiku_child_stop_inferior()\n")); err = debug_thread(threadID); if (err != B_OK) { printf_unfiltered ("Failed to stop thread %ld: %s\n", threadID, strerror(err)); return; } }
status_t LocalDebuggerInterface::StopThread(thread_id thread) { return debug_thread(thread); }
static void haiku_init_child_debugging (thread_id threadID, bool debugThread) { thread_info threadInfo; status_t result; port_id nubPort; // get a thread info result = get_thread_info(threadID, &threadInfo); if (result != B_OK) error("Thread with ID %ld not found: %s", threadID, strerror(result)); // init our team debug structure sTeamDebugInfo.team = threadInfo.team; sTeamDebugInfo.debugger_port = -1; sTeamDebugInfo.context.nub_port = -1; sTeamDebugInfo.context.reply_port = -1; sTeamDebugInfo.threads = NULL; sTeamDebugInfo.events.head = NULL; sTeamDebugInfo.events.tail = NULL; // create the debugger port sTeamDebugInfo.debugger_port = create_port(10, "gdb debug"); if (sTeamDebugInfo.debugger_port < 0) { error("Failed to create debugger port: %s", strerror(sTeamDebugInfo.debugger_port)); } // install ourselves as the team debugger nubPort = install_team_debugger(sTeamDebugInfo.team, sTeamDebugInfo.debugger_port); if (nubPort < 0) { error("Failed to install ourselves as debugger for team %ld: %s", sTeamDebugInfo.team, strerror(nubPort)); } // get the nub thread { team_info teamInfo; result = get_team_info(sTeamDebugInfo.team, &teamInfo); if (result != B_OK) { error("Failed to get info for team %ld: %s\n", sTeamDebugInfo.team, strerror(result)); } sTeamDebugInfo.nub_thread = teamInfo.debugger_nub_thread; } // init the debug context result = init_debug_context(&sTeamDebugInfo.context, sTeamDebugInfo.team, nubPort); if (result != B_OK) { error("Failed to init debug context for team %ld: %s\n", sTeamDebugInfo.team, strerror(result)); } // start debugging the thread if (debugThread) { result = debug_thread(threadID); if (result != B_OK) { error("Failed to start debugging thread %ld: %s", threadID, strerror(result)); } } // set the team debug flags { debug_nub_set_team_flags message; message.flags = B_TEAM_DEBUG_SIGNALS /*| B_TEAM_DEBUG_PRE_SYSCALL | B_TEAM_DEBUG_POST_SYSCALL*/ | B_TEAM_DEBUG_TEAM_CREATION | B_TEAM_DEBUG_THREADS | B_TEAM_DEBUG_IMAGES; // TODO: We probably don't need all events haiku_send_debugger_message(&sTeamDebugInfo, B_DEBUG_MESSAGE_SET_TEAM_FLAGS, &message, sizeof(message), NULL, 0); } // the fun can start: push the target and init the rest push_target(sHaikuTarget); haiku_init_thread_list(&sTeamDebugInfo); haiku_init_image_list(&sTeamDebugInfo); disable_breakpoints_in_shlibs (1); // child_clear_solibs (); // TODO: Implement? Do we need this? clear_proceed_status (); init_wait_for_inferior (); target_terminal_init (); target_terminal_inferior (); }
status_t thread_debug_thread(void *arg) { Tdebug_thead_param* param = (Tdebug_thead_param*) arg; #ifdef __HAIKU__ debug_thread(param->thread); #else // !__HAIKU__ thread_info thinfo; get_thread_info(param->thread, &thinfo); char text[4096]; sprintf(text, "db %d", int(param->thread)); system(text); if (param->sem >= 0 && thinfo.state == B_THREAD_WAITING && param->sem == thinfo.sem) { snooze(1000000); get_thread_info(param->thread, &thinfo); if (thinfo.state == B_THREAD_WAITING && param->sem == thinfo.sem && param->totalTime == thinfo.user_time + thinfo.kernel_time) { // the thread has been waiting for this semaphore since the before // the alert, not doing anything... Let's push it out of there! sem_info sinfo; thread_info thinfo; info_pack infos; if (get_sem_info(param->sem, &sinfo) == B_OK && get_thread_info(param->thread, &thinfo) == B_OK && get_team_info(thinfo.team, &infos.team_info) == B_OK) { sprintf (text, "This thread is waiting for the " "semaphore called \"%s\". As long as it waits for this " "semaphore, you won't be able to debug that thread.\n", sinfo.name); if (sinfo.team == thinfo.team) strcat(text, "This semaphore belongs to the " "thread's team.\n\nShould I release this semaphore?\n"); else { get_team_name_and_icon(infos); char moreText[1024]; sprintf(moreText, "\nWARNING! This semaphore " "belongs to the team \"%s\"!\n\nShould I release this " "semaphore anyway?\n", infos.team_name); strcat(text, moreText); } BAlert* alert = new BAlert("", text, "Cancel", "Release", NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT); alert->SetShortcut(0, B_ESCAPE); if (alert->Go()) { get_thread_info (param->thread, &thinfo); if (thinfo.state == B_THREAD_WAITING && param->sem == thinfo.sem && param->totalTime == thinfo.user_time + thinfo.kernel_time) release_sem(param->sem); else { alert = new BAlert("", "The semaphore wasn't released, " "because it wasn't necessary anymore!", "OK", NULL, NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT); alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); alert->Go(); } } } } } #endif // !__HAIKU__ delete param; return B_OK; }
static void kerext_debug_process(void *args) { PROCESS *prp; THREAD *thp, *act = actives[KERNCPU]; DEBUG *dep; struct kerargs_debug_process *kap = args; int status; int stopped; int tid; if(!(prp = lookup_pid(kap->pid))) { kererr(act, ESRCH); return; } dep = prp->debugger; if(kap->request == NTO_DEBUG_PROCESS_INFO || (prp->flags & _NTO_PF_TERMING)) { tid = 0; } else if(dep && kap->tid == 0) { tid = dep->tid + 1; } else { tid = kap->tid; } thp = 0; if(tid > 0 && !(thp = vector_search(&prp->threads, tid - 1, (kap->request == NTO_DEBUG_THREAD_INFO || kap->request == NTO_DEBUG_STOP) ? (unsigned *)&tid : 0))) { kererr(act, ESRCH); return; } stopped = 0; if(thp && ((thp->flags & _NTO_TF_TO_BE_STOPPED) || (thp->state != STATE_RUNNING && thp->state != STATE_READY))) { stopped = 1; } status = EINVAL; switch(kap->request) { case NTO_DEBUG_PROCESS_INFO: // pid:na:debug_process_t status = debug_process(prp, &kap->data->process); break; case NTO_DEBUG_THREAD_INFO: // pid:tid:debug_thread_t status = debug_thread(prp, thp, &kap->data->thread); break; case NTO_DEBUG_GET_GREG: // pid:tid:debug_greg_t if(thp) { memcpy(&kap->data->greg, &thp->reg, sizeof thp->reg); status = EOK; } break; case NTO_DEBUG_SET_GREG: // pid:tid:debug_greg_t if(stopped) { lock_kernel(); cpu_greg_load(thp, (CPU_REGISTERS *)&kap->data->greg); status = EOK; } break; case NTO_DEBUG_GET_FPREG: // pid:tid:debug_fpreg_t if(thp) { FPU_REGISTERS *fpudata = FPUDATA_PTR(thp->fpudata); int cpu = FPUDATA_CPU(thp->fpudata); status = ENXIO; if(fpudata) { if(FPUDATA_INUSE(thp->fpudata) && cpu != KERNCPU) { // In use on another CPU; send ipi, restart kernel call SENDIPI(cpu, IPI_CONTEXT_SAVE); KERCALL_RESTART(act); return; } if(actives_fpu[thp->runcpu] == thp) { if(KERNCPU == thp->runcpu) { cpu_force_fpu_save(thp); actives_fpu[KERNCPU] = NULL; } else { // We should not get here crash(); } } memcpy(&kap->data->fpreg, fpudata, sizeof *fpudata); status = EOK; } else if(thp->un.lcl.tls && thp->un.lcl.tls->__fpuemu_data) { // @@@ NEED TO FIND PROPER SIZE OF EMULATOR DATA memcpy(&kap->data->fpreg, thp->un.lcl.tls->__fpuemu_data, sizeof(*fpudata) + 256); status = EOK; } } break; case NTO_DEBUG_SET_FPREG: // pid:tid:debug_fpreg_t if(thp && stopped) { FPU_REGISTERS *fpudata = FPUDATA_PTR(thp->fpudata); int cpu = FPUDATA_CPU(thp->fpudata); status = ENXIO; if(thp->fpudata) { if(FPUDATA_INUSE(thp->fpudata) && cpu != KERNCPU) { // In use on another CPU; send ipi, restart kernel call SENDIPI(cpu, IPI_CONTEXT_SAVE); KERCALL_RESTART(act); return; } if(actives_fpu[thp->runcpu] == thp) { if(KERNCPU == thp->runcpu) { cpu_force_fpu_save(thp); actives_fpu[KERNCPU] = NULL; } else { // We should not get here crash(); } } memcpy(fpudata, &kap->data->fpreg, sizeof *fpudata); status = EOK; } else if(thp->un.lcl.tls && thp->un.lcl.tls->__fpuemu_data) { // @@@ NEED TO FIND PROPER SIZE OF EMULATOR DATA memcpy(thp->un.lcl.tls->__fpuemu_data, &kap->data->fpreg, sizeof(*fpudata) + 256); status = EOK; } } break; case NTO_DEBUG_STOP: // pid:na:na if(dep) { status = debug_stop(prp); } break; case NTO_DEBUG_RUN: // pid:tid:debug_run_t if(dep && stopped) { status = debug_run(prp, &kap->data->run); } break; case NTO_DEBUG_CURTHREAD: // pid:tid:NULL if(dep) { lock_kernel(); SETKSTATUS(act, dep->tid + 1); if(thp) { dep->tid = thp->tid; } return; } break; case NTO_DEBUG_FREEZE: // pid:tid:NULL if(thp == NULL){ status = EINVAL; break; } if(stopped) { lock_kernel(); thp->flags |= _NTO_TF_FROZEN; } break; case NTO_DEBUG_THAW: // pid:tid:NULL if(thp == NULL){ status = EINVAL; break; } if(stopped) { lock_kernel(); thp->flags &= ~_NTO_TF_FROZEN; } break; case NTO_DEBUG_BREAK: // pid:na:debug_break_t if(dep && stopped) { status = debug_break(prp, &kap->data->brk); } break; case NTO_DEBUG_GET_BREAKLIST: // pid:na:debug_breaklist_t status = debug_break_list(prp, &kap->data->brklist); break; case NTO_DEBUG_SET_FLAG: // pid:na:uint32_t if(dep && !(kap->data->flags & ~_DEBUG_FLAG_MASK)) { lock_kernel(); dep->flags |= kap->data->flags; } break; case NTO_DEBUG_CLEAR_FLAG: // pid:na:uint32_t if(dep && !(kap->data->flags & ~_DEBUG_FLAG_MASK)) { lock_kernel(); dep->flags &= ~kap->data->flags; } break; case NTO_DEBUG_GET_ALTREG: // pid:tid:debug_altreg_t if(thp) { status = cpu_debug_get_altregs(thp, &kap->data->altreg); } break; case NTO_DEBUG_SET_ALTREG: // pid:tid:debug_altreg_t if(thp) { status = cpu_debug_set_altregs(thp, &kap->data->altreg); } break; case NTO_DEBUG_GET_PERFREG: if ( thp ) { status = cpu_debug_get_perfregs(thp, &kap->data->perfreg); } break; case NTO_DEBUG_SET_PERFREG: if ( thp && !stopped ) status = EINVAL; else { if ( (kap->data->flags & ~PERFREGS_ENABLED_FLAG) == cpu_perfreg_id() ) { status = cpu_debug_set_perfregs(thp, &kap->data->perfreg); } else status = ENODEV; } break; } if(status != EOK) { kererr(act, status); } else { lock_kernel(); SETKSTATUS(act, 0); } }
status_t TeamDebugger::Init(team_id teamID, thread_id threadID, bool stopInMain) { bool targetIsLocal = true; // TODO: Support non-local targets! // the first thing we want to do when running PostMessage(MSG_LOAD_SETTINGS); fTeamID = teamID; // create debugger interface fDebuggerInterface = new(std::nothrow) DebuggerInterface(fTeamID); if (fDebuggerInterface == NULL) return B_NO_MEMORY; status_t error = fDebuggerInterface->Init(); if (error != B_OK) return error; // create file manager fFileManager = new(std::nothrow) FileManager; if (fFileManager == NULL) return B_NO_MEMORY; error = fFileManager->Init(targetIsLocal); if (error != B_OK) return error; // create team debug info TeamDebugInfo* teamDebugInfo = new(std::nothrow) TeamDebugInfo( fDebuggerInterface, fDebuggerInterface->GetArchitecture(), fFileManager); if (teamDebugInfo == NULL) return B_NO_MEMORY; BReference<TeamDebugInfo> teamDebugInfoReference(teamDebugInfo); error = teamDebugInfo->Init(); if (error != B_OK) return error; // check whether the team exists at all // TODO: That should be done in the debugger interface! team_info teamInfo; error = get_team_info(fTeamID, &teamInfo); if (error != B_OK) return error; // create a team object fTeam = new(std::nothrow) ::Team(fTeamID, fDebuggerInterface, fDebuggerInterface->GetArchitecture(), teamDebugInfo, teamDebugInfo); if (fTeam == NULL) return B_NO_MEMORY; error = fTeam->Init(); if (error != B_OK) return error; fTeam->SetName(teamInfo.args); // TODO: Set a better name! fTeam->AddListener(this); // init thread handler table error = fThreadHandlers.Init(); if (error != B_OK) return error; // create image handler table fImageHandlers = new(std::nothrow) ImageHandlerTable; if (fImageHandlers == NULL) return B_NO_MEMORY; error = fImageHandlers->Init(); if (error != B_OK) return error; // create our worker fWorker = new(std::nothrow) Worker; if (fWorker == NULL) return B_NO_MEMORY; error = fWorker->Init(); if (error != B_OK) return error; // create the breakpoint manager fBreakpointManager = new(std::nothrow) BreakpointManager(fTeam, fDebuggerInterface); if (fBreakpointManager == NULL) return B_NO_MEMORY; error = fBreakpointManager->Init(); if (error != B_OK) return error; // create the memory block manager fMemoryBlockManager = new(std::nothrow) TeamMemoryBlockManager(); if (fMemoryBlockManager == NULL) return B_NO_MEMORY; error = fMemoryBlockManager->Init(); if (error != B_OK) return error; // set team debugging flags fDebuggerInterface->SetTeamDebuggingFlags( B_TEAM_DEBUG_THREADS | B_TEAM_DEBUG_IMAGES); // get the initial state of the team AutoLocker< ::Team> teamLocker(fTeam); ThreadHandler* mainThreadHandler = NULL; { BObjectList<ThreadInfo> threadInfos(20, true); status_t error = fDebuggerInterface->GetThreadInfos(threadInfos); for (int32 i = 0; ThreadInfo* info = threadInfos.ItemAt(i); i++) { ::Thread* thread; error = fTeam->AddThread(*info, &thread); if (error != B_OK) return error; ThreadHandler* handler = new(std::nothrow) ThreadHandler(thread, fWorker, fDebuggerInterface, fBreakpointManager); if (handler == NULL) return B_NO_MEMORY; fThreadHandlers.Insert(handler); if (thread->IsMainThread()) mainThreadHandler = handler; handler->Init(); } } Image* appImage = NULL; { BObjectList<ImageInfo> imageInfos(20, true); status_t error = fDebuggerInterface->GetImageInfos(imageInfos); for (int32 i = 0; ImageInfo* info = imageInfos.ItemAt(i); i++) { Image* image; error = _AddImage(*info, &image); if (error != B_OK) return error; if (image->Type() == B_APP_IMAGE) appImage = image; ImageDebugInfoRequested(image); } } // create the debug event listener char buffer[128]; snprintf(buffer, sizeof(buffer), "team %ld debug listener", fTeamID); fDebugEventListener = spawn_thread(_DebugEventListenerEntry, buffer, B_NORMAL_PRIORITY, this); if (fDebugEventListener < 0) return fDebugEventListener; resume_thread(fDebugEventListener); // run looper thread_id looperThread = Run(); if (looperThread < 0) return looperThread; // init the UI error = fUserInterface->Init(fTeam, this); if (error != B_OK) { ERROR("Error: Failed to init the UI: %s\n", strerror(error)); return error; } // if requested, stop the given thread if (threadID >= 0) { if (stopInMain) { SymbolInfo symbolInfo; if (appImage != NULL && mainThreadHandler != NULL && fDebuggerInterface->GetSymbolInfo( fTeam->ID(), appImage->ID(), "main", B_SYMBOL_TYPE_TEXT, symbolInfo) == B_OK) { mainThreadHandler->SetBreakpointAndRun(symbolInfo.Address()); } } else { debug_thread(threadID); // TODO: Superfluous, if the thread is already stopped. } } fListener->TeamDebuggerStarted(this); return B_OK; }