vfs: check userland buffers before reading them.
[haiku.git] / src / apps / debugger / user_interface / cli / CliContext.cpp
blobfa287fc03fe3bea3aa61dc95dfb68c4d99e78dfc
1 /*
2 * Copyright 2012-2016, Rene Gollent, rene@gollent.com.
3 * Copyright 2012, Ingo Weinhold, ingo_weinhold@gmx.de.
4 * Distributed under the terms of the MIT License.
5 */
8 #include "CliContext.h"
10 #include <AutoDeleter.h>
11 #include <AutoLocker.h>
13 #include "StackTrace.h"
14 #include "UserInterface.h"
15 #include "ValueNodeManager.h"
16 #include "Variable.h"
19 // NOTE: This is a simple work-around for EditLine not having any kind of user
20 // data field. Hence in _GetPrompt() we don't have access to the context object.
21 // ATM only one CLI is possible in Debugger, so a static variable works well
22 // enough. Should that ever change, we would need a thread-safe
23 // EditLine* -> CliContext* map.
24 static CliContext* sCurrentContext;
27 // #pragma mark - Event
30 struct CliContext::Event : DoublyLinkedListLinkImpl<CliContext::Event> {
31 Event(int type, Thread* thread = NULL, TeamMemoryBlock* block = NULL,
32 ExpressionInfo* info = NULL, status_t expressionResult = B_OK,
33 ExpressionResult* expressionValue = NULL)
35 fType(type),
36 fThreadReference(thread),
37 fMemoryBlockReference(block),
38 fExpressionInfo(info),
39 fExpressionResult(expressionResult),
40 fExpressionValue(expressionValue)
44 int Type() const
46 return fType;
49 Thread* GetThread() const
51 return fThreadReference.Get();
54 TeamMemoryBlock* GetMemoryBlock() const
56 return fMemoryBlockReference.Get();
59 ExpressionInfo* GetExpressionInfo() const
61 return fExpressionInfo;
64 status_t GetExpressionResult() const
66 return fExpressionResult;
69 ExpressionResult* GetExpressionValue() const
71 return fExpressionValue.Get();
75 private:
76 int fType;
77 BReference<Thread> fThreadReference;
78 BReference<TeamMemoryBlock> fMemoryBlockReference;
79 BReference<ExpressionInfo> fExpressionInfo;
80 status_t fExpressionResult;
81 BReference<ExpressionResult> fExpressionValue;
85 // #pragma mark - CliContext
88 CliContext::CliContext()
90 fLock("CliContext"),
91 fTeam(NULL),
92 fListener(NULL),
93 fNodeManager(NULL),
94 fEditLine(NULL),
95 fHistory(NULL),
96 fPrompt(NULL),
97 fBlockingSemaphore(-1),
98 fInputLoopWaitingForEvents(0),
99 fEventsOccurred(0),
100 fInputLoopWaiting(false),
101 fTerminating(false),
102 fCurrentThread(NULL),
103 fCurrentStackTrace(NULL),
104 fCurrentStackFrameIndex(-1),
105 fCurrentBlock(NULL),
106 fExpressionInfo(NULL),
107 fExpressionResult(B_OK),
108 fExpressionValue(NULL)
110 sCurrentContext = this;
114 CliContext::~CliContext()
116 Cleanup();
117 sCurrentContext = NULL;
119 if (fBlockingSemaphore >= 0)
120 delete_sem(fBlockingSemaphore);
124 status_t
125 CliContext::Init(Team* team, UserInterfaceListener* listener)
127 fTeam = team;
128 fListener = listener;
130 fTeam->AddListener(this);
132 status_t error = fLock.InitCheck();
133 if (error != B_OK)
134 return error;
136 fBlockingSemaphore = create_sem(0, "CliContext block");
137 if (fBlockingSemaphore < 0)
138 return fBlockingSemaphore;
140 fEditLine = el_init("Debugger", stdin, stdout, stderr);
141 if (fEditLine == NULL)
142 return B_ERROR;
144 fHistory = history_init();
145 if (fHistory == NULL)
146 return B_ERROR;
148 HistEvent historyEvent;
149 history(fHistory, &historyEvent, H_SETSIZE, 100);
151 el_set(fEditLine, EL_HIST, &history, fHistory);
152 el_set(fEditLine, EL_EDITOR, "emacs");
153 el_set(fEditLine, EL_PROMPT, &_GetPrompt);
155 fNodeManager = new(std::nothrow) ValueNodeManager();
156 if (fNodeManager == NULL)
157 return B_NO_MEMORY;
158 fNodeManager->AddListener(this);
160 fExpressionInfo = new(std::nothrow) ExpressionInfo();
161 if (fExpressionInfo == NULL)
162 return B_NO_MEMORY;
163 fExpressionInfo->AddListener(this);
165 return B_OK;
169 void
170 CliContext::Cleanup()
172 Terminating();
174 while (Event* event = fPendingEvents.RemoveHead())
175 delete event;
177 if (fEditLine != NULL) {
178 el_end(fEditLine);
179 fEditLine = NULL;
182 if (fHistory != NULL) {
183 history_end(fHistory);
184 fHistory = NULL;
187 if (fTeam != NULL) {
188 fTeam->RemoveListener(this);
189 fTeam = NULL;
192 if (fNodeManager != NULL) {
193 fNodeManager->ReleaseReference();
194 fNodeManager = NULL;
197 if (fCurrentBlock != NULL) {
198 fCurrentBlock->ReleaseReference();
199 fCurrentBlock = NULL;
202 if (fExpressionInfo != NULL) {
203 fExpressionInfo->ReleaseReference();
204 fExpressionInfo = NULL;
209 void
210 CliContext::Terminating()
212 AutoLocker<BLocker> locker(fLock);
214 fTerminating = true;
215 _SignalInputLoop(EVENT_QUIT);
217 // TODO: Signal the input loop, should it be in PromptUser()!
221 thread_id
222 CliContext::CurrentThreadID() const
224 return fCurrentThread != NULL ? fCurrentThread->ID() : -1;
228 void
229 CliContext::SetCurrentThread(Thread* thread)
231 AutoLocker<BLocker> locker(fLock);
233 if (fCurrentThread != NULL)
234 fCurrentThread->ReleaseReference();
236 fCurrentThread = thread;
238 if (fCurrentStackTrace != NULL) {
239 fCurrentStackTrace->ReleaseReference();
240 fCurrentStackTrace = NULL;
241 fCurrentStackFrameIndex = -1;
242 fNodeManager->SetStackFrame(NULL, NULL);
245 if (fCurrentThread != NULL) {
246 fCurrentThread->AcquireReference();
247 StackTrace* stackTrace = fCurrentThread->GetStackTrace();
248 // if the thread's stack trace has already been loaded,
249 // set it, otherwise we'll set it when we process the thread's
250 // stack trace changed event.
251 if (stackTrace != NULL) {
252 fCurrentStackTrace = stackTrace;
253 fCurrentStackTrace->AcquireReference();
254 SetCurrentStackFrameIndex(0);
260 void
261 CliContext::PrintCurrentThread()
263 AutoLocker<Team> teamLocker(fTeam);
265 if (fCurrentThread != NULL) {
266 printf("current thread: %" B_PRId32 " \"%s\"\n", fCurrentThread->ID(),
267 fCurrentThread->Name());
268 } else
269 printf("no current thread\n");
273 void
274 CliContext::SetCurrentStackFrameIndex(int32 index)
276 AutoLocker<BLocker> locker(fLock);
278 if (fCurrentStackTrace == NULL)
279 return;
280 else if (index < 0 || index >= fCurrentStackTrace->CountFrames())
281 return;
283 fCurrentStackFrameIndex = index;
285 StackFrame* frame = fCurrentStackTrace->FrameAt(index);
286 if (frame != NULL)
287 fNodeManager->SetStackFrame(fCurrentThread, frame);
291 const char*
292 CliContext::PromptUser(const char* prompt)
294 fPrompt = prompt;
296 int count;
297 const char* line = el_gets(fEditLine, &count);
299 fPrompt = NULL;
301 ProcessPendingEvents();
303 return line;
307 void
308 CliContext::AddLineToInputHistory(const char* line)
310 HistEvent historyEvent;
311 history(fHistory, &historyEvent, H_ENTER, line);
315 void
316 CliContext::QuitSession(bool killTeam)
318 _PrepareToWaitForEvents(EVENT_QUIT);
320 fListener->UserInterfaceQuitRequested(
321 killTeam
322 ? UserInterfaceListener::QUIT_OPTION_ASK_KILL_TEAM
323 : UserInterfaceListener::QUIT_OPTION_ASK_RESUME_TEAM);
325 _WaitForEvents();
329 void
330 CliContext::WaitForThreadOrUser()
332 ProcessPendingEvents();
334 // TODO: Deal with SIGINT as well!
335 for (;;) {
336 _PrepareToWaitForEvents(
337 EVENT_USER_INTERRUPT | EVENT_THREAD_STOPPED);
339 // check whether there are any threads stopped already
340 Thread* stoppedThread = NULL;
341 BReference<Thread> stoppedThreadReference;
343 AutoLocker<Team> teamLocker(fTeam);
345 for (ThreadList::ConstIterator it = fTeam->Threads().GetIterator();
346 Thread* thread = it.Next();) {
347 if (thread->State() == THREAD_STATE_STOPPED) {
348 stoppedThread = thread;
349 stoppedThreadReference.SetTo(thread);
350 break;
354 teamLocker.Unlock();
356 if (stoppedThread != NULL) {
357 if (fCurrentThread == NULL)
358 SetCurrentThread(stoppedThread);
360 _SignalInputLoop(EVENT_THREAD_STOPPED);
363 uint32 events = _WaitForEvents();
364 if ((events & EVENT_QUIT) != 0 || stoppedThread != NULL) {
365 ProcessPendingEvents();
366 return;
372 void
373 CliContext::WaitForEvents(int32 eventMask)
375 for (;;) {
376 _PrepareToWaitForEvents(eventMask | EVENT_USER_INTERRUPT);
377 uint32 events = fEventsOccurred;
378 if ((events & eventMask) == 0) {
379 events = _WaitForEvents();
382 if ((events & EVENT_QUIT) != 0 || (events & eventMask) != 0) {
383 _SignalInputLoop(eventMask);
384 ProcessPendingEvents();
385 return;
391 void
392 CliContext::ProcessPendingEvents()
394 AutoLocker<Team> teamLocker(fTeam);
396 for (;;) {
397 // get the next event
398 AutoLocker<BLocker> locker(fLock);
399 Event* event = fPendingEvents.RemoveHead();
400 locker.Unlock();
401 if (event == NULL)
402 break;
403 ObjectDeleter<Event> eventDeleter(event);
405 // process the event
406 Thread* thread = event->GetThread();
408 switch (event->Type()) {
409 case EVENT_QUIT:
410 case EVENT_DEBUG_REPORT_CHANGED:
411 case EVENT_USER_INTERRUPT:
412 break;
413 case EVENT_THREAD_ADDED:
414 printf("[new thread: %" B_PRId32 " \"%s\"]\n", thread->ID(),
415 thread->Name());
416 break;
417 case EVENT_THREAD_REMOVED:
418 printf("[thread terminated: %" B_PRId32 " \"%s\"]\n",
419 thread->ID(), thread->Name());
420 break;
421 case EVENT_THREAD_STOPPED:
422 printf("[thread stopped: %" B_PRId32 " \"%s\"]\n",
423 thread->ID(), thread->Name());
424 break;
425 case EVENT_THREAD_STACK_TRACE_CHANGED:
426 if (thread == fCurrentThread) {
427 fCurrentStackTrace = thread->GetStackTrace();
428 fCurrentStackTrace->AcquireReference();
429 SetCurrentStackFrameIndex(0);
431 break;
432 case EVENT_TEAM_MEMORY_BLOCK_RETRIEVED:
433 if (fCurrentBlock != NULL) {
434 fCurrentBlock->ReleaseReference();
435 fCurrentBlock = NULL;
437 fCurrentBlock = event->GetMemoryBlock();
438 break;
439 case EVENT_EXPRESSION_EVALUATED:
440 fExpressionResult = event->GetExpressionResult();
441 if (fExpressionValue != NULL) {
442 fExpressionValue->ReleaseReference();
443 fExpressionValue = NULL;
445 fExpressionValue = event->GetExpressionValue();
446 if (fExpressionValue != NULL)
447 fExpressionValue->AcquireReference();
448 break;
454 void
455 CliContext::ThreadAdded(const Team::ThreadEvent& threadEvent)
457 _QueueEvent(
458 new(std::nothrow) Event(EVENT_THREAD_ADDED, threadEvent.GetThread()));
459 _SignalInputLoop(EVENT_THREAD_ADDED);
463 void
464 CliContext::ThreadRemoved(const Team::ThreadEvent& threadEvent)
466 _QueueEvent(
467 new(std::nothrow) Event(EVENT_THREAD_REMOVED, threadEvent.GetThread()));
468 _SignalInputLoop(EVENT_THREAD_REMOVED);
472 void
473 CliContext::ThreadStateChanged(const Team::ThreadEvent& threadEvent)
475 if (threadEvent.GetThread()->State() != THREAD_STATE_STOPPED)
476 return;
478 _QueueEvent(
479 new(std::nothrow) Event(EVENT_THREAD_STOPPED, threadEvent.GetThread()));
480 _SignalInputLoop(EVENT_THREAD_STOPPED);
484 void
485 CliContext::ThreadStackTraceChanged(const Team::ThreadEvent& threadEvent)
487 if (threadEvent.GetThread()->State() != THREAD_STATE_STOPPED)
488 return;
490 _QueueEvent(
491 new(std::nothrow) Event(EVENT_THREAD_STACK_TRACE_CHANGED,
492 threadEvent.GetThread()));
493 _SignalInputLoop(EVENT_THREAD_STACK_TRACE_CHANGED);
497 void
498 CliContext::ExpressionEvaluated(ExpressionInfo* info, status_t result,
499 ExpressionResult* value)
501 _QueueEvent(
502 new(std::nothrow) Event(EVENT_EXPRESSION_EVALUATED,
503 NULL, NULL, info, result, value));
504 _SignalInputLoop(EVENT_EXPRESSION_EVALUATED);
508 void
509 CliContext::DebugReportChanged(const Team::DebugReportEvent& event)
511 if (event.GetFinalStatus() == B_OK) {
512 printf("Successfully saved debug report to %s\n",
513 event.GetReportPath());
514 } else {
515 fprintf(stderr, "Failed to write debug report: %s\n", strerror(
516 event.GetFinalStatus()));
519 _QueueEvent(new(std::nothrow) Event(EVENT_DEBUG_REPORT_CHANGED));
520 _SignalInputLoop(EVENT_DEBUG_REPORT_CHANGED);
524 void
525 CliContext::CoreFileChanged(const Team::CoreFileChangedEvent& event)
527 printf("Successfully saved core file to %s\n",
528 event.GetTargetPath());
530 _QueueEvent(new(std::nothrow) Event(EVENT_CORE_FILE_CHANGED));
531 _SignalInputLoop(EVENT_CORE_FILE_CHANGED);
535 void
536 CliContext::MemoryBlockRetrieved(TeamMemoryBlock* block)
538 _QueueEvent(
539 new(std::nothrow) Event(EVENT_TEAM_MEMORY_BLOCK_RETRIEVED,
540 NULL, block));
541 _SignalInputLoop(EVENT_TEAM_MEMORY_BLOCK_RETRIEVED);
545 void
546 CliContext::ValueNodeChanged(ValueNodeChild* nodeChild, ValueNode* oldNode,
547 ValueNode* newNode)
549 _SignalInputLoop(EVENT_VALUE_NODE_CHANGED);
553 void
554 CliContext::ValueNodeChildrenCreated(ValueNode* node)
556 _SignalInputLoop(EVENT_VALUE_NODE_CHANGED);
560 void
561 CliContext::ValueNodeChildrenDeleted(ValueNode* node)
563 _SignalInputLoop(EVENT_VALUE_NODE_CHANGED);
567 void
568 CliContext::ValueNodeValueChanged(ValueNode* oldNode)
570 _SignalInputLoop(EVENT_VALUE_NODE_CHANGED);
574 void
575 CliContext::_QueueEvent(Event* event)
577 if (event == NULL) {
578 // no memory -- can't do anything about it
579 return;
582 AutoLocker<BLocker> locker(fLock);
583 fPendingEvents.Add(event);
587 void
588 CliContext::_PrepareToWaitForEvents(uint32 eventMask)
590 // Set the events we're going to wait for -- always wait for "quit".
591 AutoLocker<BLocker> locker(fLock);
592 fInputLoopWaitingForEvents = eventMask | EVENT_QUIT;
593 fEventsOccurred = fTerminating ? EVENT_QUIT : 0;
597 uint32
598 CliContext::_WaitForEvents()
600 AutoLocker<BLocker> locker(fLock);
602 if (fEventsOccurred == 0) {
603 sem_id blockingSemaphore = fBlockingSemaphore;
604 fInputLoopWaiting = true;
606 locker.Unlock();
608 while (acquire_sem(blockingSemaphore) == B_INTERRUPTED) {
611 locker.Lock();
614 uint32 events = fEventsOccurred;
615 fEventsOccurred = 0;
616 return events;
620 void
621 CliContext::_SignalInputLoop(uint32 events)
623 AutoLocker<BLocker> locker(fLock);
625 if ((fInputLoopWaitingForEvents & events) == 0)
626 return;
628 fEventsOccurred = fInputLoopWaitingForEvents & events;
629 fInputLoopWaitingForEvents = 0;
631 if (fInputLoopWaiting) {
632 fInputLoopWaiting = false;
633 release_sem(fBlockingSemaphore);
638 /*static*/ const char*
639 CliContext::_GetPrompt(EditLine* editLine)
641 return sCurrentContext != NULL ? sCurrentContext->fPrompt : NULL;