blob: 9262d6b331c1b2e3e775276d2edaa24f465c2a1d [file] [log] [blame]
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001//===-- MachTask.cpp --------------------------------------------*- C++ -*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//----------------------------------------------------------------------
10//
11// MachTask.cpp
12// debugserver
13//
14// Created by Greg Clayton on 12/5/08.
15//
16//===----------------------------------------------------------------------===//
17
18#include "MachTask.h"
19
20// C Includes
21
22#include <mach-o/dyld_images.h>
23#include <mach/mach_vm.h>
24
25// C++ Includes
26// Other libraries and framework includes
27// Project includes
28#include "CFUtils.h"
29#include "DNB.h"
30#include "DNBError.h"
31#include "DNBLog.h"
32#include "MachProcess.h"
33#include "DNBDataRef.h"
34
35#if defined (__arm__)
36
37#include <CoreFoundation/CoreFoundation.h>
38#include <SpringBoardServices/SpringBoardServer.h>
39#include <SpringBoardServices/SBSWatchdogAssertion.h>
40
41#endif
42
43//----------------------------------------------------------------------
44// MachTask constructor
45//----------------------------------------------------------------------
46MachTask::MachTask(MachProcess *process) :
47 m_process (process),
48 m_task (TASK_NULL),
49 m_vm_memory (),
50 m_exception_thread (0),
51 m_exception_port (MACH_PORT_NULL)
52{
53 memset(&m_exc_port_info, 0, sizeof(m_exc_port_info));
54
55}
56
57//----------------------------------------------------------------------
58// Destructor
59//----------------------------------------------------------------------
60MachTask::~MachTask()
61{
62 Clear();
63}
64
65
66//----------------------------------------------------------------------
67// MachTask::Suspend
68//----------------------------------------------------------------------
69kern_return_t
70MachTask::Suspend()
71{
72 DNBError err;
73 task_t task = TaskPort();
74 err = ::task_suspend (task);
75 if (DNBLogCheckLogBit(LOG_TASK) || err.Fail())
76 err.LogThreaded("::task_suspend ( target_task = 0x%4.4x )", task);
77 return err.Error();
78}
79
80
81//----------------------------------------------------------------------
82// MachTask::Resume
83//----------------------------------------------------------------------
84kern_return_t
85MachTask::Resume()
86{
87 struct task_basic_info task_info;
88 task_t task = TaskPort();
89
90 DNBError err;
91 err = BasicInfo(task, &task_info);
92
93 if (err.Success())
94 {
95 integer_t i;
96 for (i=0; i<task_info.suspend_count; i++)
97 {
98 err = ::task_resume (task);
99 if (DNBLogCheckLogBit(LOG_TASK) || err.Fail())
100 err.LogThreaded("::task_resume ( target_task = 0x%4.4x )", task);
101 }
102 }
103 return err.Error();
104}
105
106//----------------------------------------------------------------------
107// MachTask::ExceptionPort
108//----------------------------------------------------------------------
109mach_port_t
110MachTask::ExceptionPort() const
111{
112 return m_exception_port;
113}
114
115//----------------------------------------------------------------------
116// MachTask::ExceptionPortIsValid
117//----------------------------------------------------------------------
118bool
119MachTask::ExceptionPortIsValid() const
120{
121 return MACH_PORT_VALID(m_exception_port);
122}
123
124
125//----------------------------------------------------------------------
126// MachTask::Clear
127//----------------------------------------------------------------------
128void
129MachTask::Clear()
130{
131 // Do any cleanup needed for this task
132 m_task = TASK_NULL;
133 m_exception_thread = 0;
134 m_exception_port = MACH_PORT_NULL;
135
136}
137
138
139//----------------------------------------------------------------------
140// MachTask::SaveExceptionPortInfo
141//----------------------------------------------------------------------
142kern_return_t
143MachTask::SaveExceptionPortInfo()
144{
145 return m_exc_port_info.Save(TaskPort());
146}
147
148//----------------------------------------------------------------------
149// MachTask::RestoreExceptionPortInfo
150//----------------------------------------------------------------------
151kern_return_t
152MachTask::RestoreExceptionPortInfo()
153{
154 return m_exc_port_info.Restore(TaskPort());
155}
156
157
158//----------------------------------------------------------------------
159// MachTask::ReadMemory
160//----------------------------------------------------------------------
161nub_size_t
162MachTask::ReadMemory (nub_addr_t addr, nub_size_t size, void *buf)
163{
164 nub_size_t n = 0;
165 task_t task = TaskPort();
166 if (task != TASK_NULL)
167 {
168 n = m_vm_memory.Read(task, addr, buf, size);
169
170 DNBLogThreadedIf(LOG_MEMORY, "MachTask::ReadMemory ( addr = 0x%8.8llx, size = %zu, buf = %8.8p) => %u bytes read", (uint64_t)addr, size, buf, n);
171 if (DNBLogCheckLogBit(LOG_MEMORY_DATA_LONG) || (DNBLogCheckLogBit(LOG_MEMORY_DATA_SHORT) && size <= 8))
172 {
173 DNBDataRef data((uint8_t*)buf, n, false);
174 data.Dump(0, n, addr, DNBDataRef::TypeUInt8, 16);
175 }
176 }
177 return n;
178}
179
180
181//----------------------------------------------------------------------
182// MachTask::WriteMemory
183//----------------------------------------------------------------------
184nub_size_t
185MachTask::WriteMemory (nub_addr_t addr, nub_size_t size, const void *buf)
186{
187 nub_size_t n = 0;
188 task_t task = TaskPort();
189 if (task != TASK_NULL)
190 {
191 n = m_vm_memory.Write(task, addr, buf, size);
192 DNBLogThreadedIf(LOG_MEMORY, "MachTask::WriteMemory ( addr = 0x%8.8llx, size = %zu, buf = %8.8p) => %u bytes written", (uint64_t)addr, size, buf, n);
193 if (DNBLogCheckLogBit(LOG_MEMORY_DATA_LONG) || (DNBLogCheckLogBit(LOG_MEMORY_DATA_SHORT) && size <= 8))
194 {
195 DNBDataRef data((uint8_t*)buf, n, false);
196 data.Dump(0, n, addr, DNBDataRef::TypeUInt8, 16);
197 }
198 }
199 return n;
200}
201
202//----------------------------------------------------------------------
203// MachTask::TaskPortForProcessID
204//----------------------------------------------------------------------
205task_t
206MachTask::TaskPortForProcessID (DNBError &err)
207{
208 if (m_task == TASK_NULL && m_process != NULL)
209 m_task = MachTask::TaskPortForProcessID(m_process->ProcessID(), err);
210 return m_task;
211}
212
213//----------------------------------------------------------------------
214// MachTask::TaskPortForProcessID
215//----------------------------------------------------------------------
216task_t
217MachTask::TaskPortForProcessID (pid_t pid, DNBError &err)
218{
219 task_t task = TASK_NULL;
220 if (pid != INVALID_NUB_PROCESS)
221 {
222 mach_port_t task_self = mach_task_self ();
223 err = ::task_for_pid ( task_self, pid, &task);
224 if (DNBLogCheckLogBit(LOG_TASK) || err.Fail())
225 {
226 char str[1024];
227 ::snprintf (str,
228 sizeof(str),
Greg Clayton3382c2c2010-07-30 23:14:42 +0000229 "::task_for_pid ( target_tport = 0x%4.4x, pid = %d, &task ) => err = 0x%8.8x (%s)",
230 task_self,
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000231 pid,
Greg Clayton3382c2c2010-07-30 23:14:42 +0000232 err.Error(),
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000233 err.AsString() ? err.AsString() : "success");
234 if (err.Fail())
235 err.SetErrorString(str);
236 err.LogThreaded(str);
237 }
238 }
239 return task;
240}
241
242
243//----------------------------------------------------------------------
244// MachTask::BasicInfo
245//----------------------------------------------------------------------
246kern_return_t
247MachTask::BasicInfo(struct task_basic_info *info)
248{
249 return BasicInfo (TaskPort(), info);
250}
251
252//----------------------------------------------------------------------
253// MachTask::BasicInfo
254//----------------------------------------------------------------------
255kern_return_t
256MachTask::BasicInfo(task_t task, struct task_basic_info *info)
257{
258 if (info == NULL)
259 return KERN_INVALID_ARGUMENT;
260
261 DNBError err;
262 mach_msg_type_number_t count = TASK_BASIC_INFO_COUNT;
263 err = ::task_info (task, TASK_BASIC_INFO, (task_info_t)info, &count);
264 const bool log_process = DNBLogCheckLogBit(LOG_TASK);
265 if (log_process || err.Fail())
266 err.LogThreaded("::task_info ( target_task = 0x%4.4x, flavor = TASK_BASIC_INFO, task_info_out => %p, task_info_outCnt => %u )", task, info, count);
267 if (DNBLogCheckLogBit(LOG_TASK) && DNBLogCheckLogBit(LOG_VERBOSE) && err.Success())
268 {
269 float user = (float)info->user_time.seconds + (float)info->user_time.microseconds / 1000000.0f;
270 float system = (float)info->user_time.seconds + (float)info->user_time.microseconds / 1000000.0f;
271 DNBLogThreaded("task_basic_info = { suspend_count = %i, virtual_size = 0x%8.8x, resident_size = 0x%8.8x, user_time = %f, system_time = %f }",
272 info->suspend_count, info->virtual_size, info->resident_size, user, system);
273 }
274 return err.Error();
275}
276
277
278//----------------------------------------------------------------------
279// MachTask::IsValid
280//
281// Returns true if a task is a valid task port for a current process.
282//----------------------------------------------------------------------
283bool
284MachTask::IsValid () const
285{
286 return MachTask::IsValid(TaskPort());
287}
288
289//----------------------------------------------------------------------
290// MachTask::IsValid
291//
292// Returns true if a task is a valid task port for a current process.
293//----------------------------------------------------------------------
294bool
295MachTask::IsValid (task_t task)
296{
297 if (task != TASK_NULL)
298 {
299 struct task_basic_info task_info;
300 return BasicInfo(task, &task_info) == KERN_SUCCESS;
301 }
302 return false;
303}
304
305
306bool
307MachTask::StartExceptionThread(DNBError &err)
308{
309 DNBLogThreadedIf(LOG_EXCEPTIONS, "MachTask::%s ( )", __FUNCTION__);
310 task_t task = TaskPortForProcessID(err);
311 if (MachTask::IsValid(task))
312 {
313 // Got the mach port for the current process
314 mach_port_t task_self = mach_task_self ();
315
316 // Allocate an exception port that we will use to track our child process
317 err = ::mach_port_allocate (task_self, MACH_PORT_RIGHT_RECEIVE, &m_exception_port);
318 if (err.Fail())
319 return false;
320
321 // Add the ability to send messages on the new exception port
322 err = ::mach_port_insert_right (task_self, m_exception_port, m_exception_port, MACH_MSG_TYPE_MAKE_SEND);
323 if (err.Fail())
324 return false;
325
326 // Save the original state of the exception ports for our child process
327 SaveExceptionPortInfo();
328
329 // Set the ability to get all exceptions on this port
330 err = ::task_set_exception_ports (task, EXC_MASK_ALL, m_exception_port, EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, THREAD_STATE_NONE);
331 if (err.Fail())
332 return false;
333
334 // Create the exception thread
335 err = ::pthread_create (&m_exception_thread, NULL, MachTask::ExceptionThread, this);
336 return err.Success();
337 }
338 else
339 {
340 DNBLogError("MachTask::%s (): task invalid, exception thread start failed.", __FUNCTION__);
341 }
342 return false;
343}
344
345kern_return_t
346MachTask::ShutDownExcecptionThread()
347{
348 DNBError err;
349
350 err = RestoreExceptionPortInfo();
351
352 // NULL our our exception port and let our exception thread exit
353 mach_port_t exception_port = m_exception_port;
354 m_exception_port = NULL;
355
356 err.SetError(::pthread_cancel(m_exception_thread), DNBError::POSIX);
357 if (DNBLogCheckLogBit(LOG_TASK) || err.Fail())
358 err.LogThreaded("::pthread_cancel ( thread = %p )", m_exception_thread);
359
360 err.SetError(::pthread_join(m_exception_thread, NULL), DNBError::POSIX);
361 if (DNBLogCheckLogBit(LOG_TASK) || err.Fail())
362 err.LogThreaded("::pthread_join ( thread = %p, value_ptr = NULL)", m_exception_thread);
363
364 // Deallocate our exception port that we used to track our child process
365 mach_port_t task_self = mach_task_self ();
366 err = ::mach_port_deallocate (task_self, exception_port);
367 if (DNBLogCheckLogBit(LOG_TASK) || err.Fail())
368 err.LogThreaded("::mach_port_deallocate ( task = 0x%4.4x, name = 0x%4.4x )", task_self, exception_port);
369 exception_port = NULL;
370
371 return err.Error();
372}
373
374
375void *
376MachTask::ExceptionThread (void *arg)
377{
378 if (arg == NULL)
379 return NULL;
380
381 MachTask *mach_task = (MachTask*) arg;
382 MachProcess *mach_proc = mach_task->Process();
383 DNBLogThreadedIf(LOG_EXCEPTIONS, "MachTask::%s ( arg = %p ) starting thread...", __FUNCTION__, arg);
384
385 // We keep a count of the number of consecutive exceptions received so
386 // we know to grab all exceptions without a timeout. We do this to get a
387 // bunch of related exceptions on our exception port so we can process
388 // then together. When we have multiple threads, we can get an exception
389 // per thread and they will come in consecutively. The main loop in this
390 // thread can stop periodically if needed to service things related to this
391 // process.
392 // flag set in the options, so we will wait forever for an exception on
393 // our exception port. After we get one exception, we then will use the
394 // MACH_RCV_TIMEOUT option with a zero timeout to grab all other current
395 // exceptions for our process. After we have received the last pending
396 // exception, we will get a timeout which enables us to then notify
397 // our main thread that we have an exception bundle avaiable. We then wait
398 // for the main thread to tell this exception thread to start trying to get
399 // exceptions messages again and we start again with a mach_msg read with
400 // infinite timeout.
401 uint32_t num_exceptions_received = 0;
402 DNBError err;
403 task_t task = mach_task->TaskPort();
404 mach_msg_timeout_t periodic_timeout = 0;
405
406#if defined (__arm__)
407 mach_msg_timeout_t watchdog_elapsed = 0;
408 mach_msg_timeout_t watchdog_timeout = 60 * 1000;
409 pid_t pid = mach_proc->ProcessID();
410 CFReleaser<SBSWatchdogAssertionRef> watchdog;
411
412 if (mach_proc->ProcessUsingSpringBoard())
413 {
414 // Request a renewal for every 60 seconds if we attached using SpringBoard
415 watchdog.reset(::SBSWatchdogAssertionCreateForPID(NULL, pid, 60));
416 DNBLogThreadedIf(LOG_TASK, "::SBSWatchdogAssertionCreateForPID (NULL, %4.4x, 60 ) => %p", pid, watchdog.get());
417
418 if (watchdog.get())
419 {
420 ::SBSWatchdogAssertionRenew (watchdog.get());
421
422 CFTimeInterval watchdogRenewalInterval = ::SBSWatchdogAssertionGetRenewalInterval (watchdog.get());
423 DNBLogThreadedIf(LOG_TASK, "::SBSWatchdogAssertionGetRenewalInterval ( %p ) => %g seconds", watchdog.get(), watchdogRenewalInterval);
424 if (watchdogRenewalInterval > 0.0)
425 {
426 watchdog_timeout = (mach_msg_timeout_t)watchdogRenewalInterval * 1000;
427 if (watchdog_timeout > 3000)
428 watchdog_timeout -= 1000; // Give us a second to renew our timeout
429 else if (watchdog_timeout > 1000)
430 watchdog_timeout -= 250; // Give us a quarter of a second to renew our timeout
431 }
432 }
433 if (periodic_timeout == 0 || periodic_timeout > watchdog_timeout)
434 periodic_timeout = watchdog_timeout;
435 }
436#endif // #if defined (__arm__)
437
438 while (mach_task->ExceptionPortIsValid())
439 {
440 ::pthread_testcancel ();
441
442 MachException::Message exception_message;
443
444
445 if (num_exceptions_received > 0)
446 {
447 // No timeout, just receive as many exceptions as we can since we already have one and we want
448 // to get all currently available exceptions for this task
449 err = exception_message.Receive(mach_task->ExceptionPort(), MACH_RCV_MSG | MACH_RCV_INTERRUPT | MACH_RCV_TIMEOUT, 0);
450 }
451 else if (periodic_timeout > 0)
452 {
453 // We need to stop periodically in this loop, so try and get a mach message with a valid timeout (ms)
454 err = exception_message.Receive(mach_task->ExceptionPort(), MACH_RCV_MSG | MACH_RCV_INTERRUPT | MACH_RCV_TIMEOUT, periodic_timeout);
455 }
456 else
457 {
458 // We don't need to parse all current exceptions or stop periodically,
459 // just wait for an exception forever.
460 err = exception_message.Receive(mach_task->ExceptionPort(), MACH_RCV_MSG | MACH_RCV_INTERRUPT, 0);
461 }
462
463 if (err.Error() == MACH_RCV_INTERRUPTED)
464 {
465 // If we have no task port we should exit this thread
466 if (!mach_task->ExceptionPortIsValid())
467 {
468 DNBLogThreadedIf(LOG_EXCEPTIONS, "thread cancelled...");
469 break;
470 }
471
472 // Make sure our task is still valid
473 if (MachTask::IsValid(task))
474 {
475 // Task is still ok
476 DNBLogThreadedIf(LOG_EXCEPTIONS, "interrupted, but task still valid, continuing...");
477 continue;
478 }
479 else
480 {
481 DNBLogThreadedIf(LOG_EXCEPTIONS, "task has exited...");
482 mach_proc->SetState(eStateExited);
483 // Our task has died, exit the thread.
484 break;
485 }
486 }
487 else if (err.Error() == MACH_RCV_TIMED_OUT)
488 {
489 if (num_exceptions_received > 0)
490 {
491 // We were receiving all current exceptions with a timeout of zero
492 // it is time to go back to our normal looping mode
493 num_exceptions_received = 0;
494
495 // Notify our main thread we have a complete exception message
496 // bundle available.
497 mach_proc->ExceptionMessageBundleComplete();
498
499 // in case we use a timeout value when getting exceptions...
500 // Make sure our task is still valid
501 if (MachTask::IsValid(task))
502 {
503 // Task is still ok
504 DNBLogThreadedIf(LOG_EXCEPTIONS, "got a timeout, continuing...");
505 continue;
506 }
507 else
508 {
509 DNBLogThreadedIf(LOG_EXCEPTIONS, "task has exited...");
510 mach_proc->SetState(eStateExited);
511 // Our task has died, exit the thread.
512 break;
513 }
514 continue;
515 }
516
517#if defined (__arm__)
518 if (watchdog.get())
519 {
520 watchdog_elapsed += periodic_timeout;
521 if (watchdog_elapsed >= watchdog_timeout)
522 {
523 DNBLogThreadedIf(LOG_TASK, "SBSWatchdogAssertionRenew ( %p )", watchdog.get());
524 ::SBSWatchdogAssertionRenew (watchdog.get());
525 watchdog_elapsed = 0;
526 }
527 }
528#endif
529 }
530 else if (err.Error() != KERN_SUCCESS)
531 {
532 DNBLogThreadedIf(LOG_EXCEPTIONS, "got some other error, do something about it??? nah, continuing for now...");
533 // TODO: notify of error?
534 }
535 else
536 {
537 if (exception_message.CatchExceptionRaise())
538 {
539 ++num_exceptions_received;
540 mach_proc->ExceptionMessageReceived(exception_message);
541 }
542 }
543 }
544
545#if defined (__arm__)
546 if (watchdog.get())
547 {
548 // TODO: change SBSWatchdogAssertionRelease to SBSWatchdogAssertionCancel when we
549 // all are up and running on systems that support it. The SBS framework has a #define
550 // that will forward SBSWatchdogAssertionRelease to SBSWatchdogAssertionCancel for now
551 // so it should still build either way.
552 DNBLogThreadedIf(LOG_TASK, "::SBSWatchdogAssertionRelease(%p)", watchdog.get());
553 ::SBSWatchdogAssertionRelease (watchdog.get());
554 }
555#endif // #if defined (__arm__)
556
557 DNBLogThreadedIf(LOG_EXCEPTIONS, "MachTask::%s (%p): thread exiting...", __FUNCTION__, arg);
558 return NULL;
559}
560
561
562// So the TASK_DYLD_INFO used to just return the address of the all image infos
563// as a single member called "all_image_info". Then someone decided it would be
564// a good idea to rename this first member to "all_image_info_addr" and add a
565// size member called "all_image_info_size". This of course can not be detected
566// using code or #defines. So to hack around this problem, we define our own
567// version of the TASK_DYLD_INFO structure so we can guarantee what is inside it.
568
569struct hack_task_dyld_info {
570 mach_vm_address_t all_image_info_addr;
571 mach_vm_size_t all_image_info_size;
572};
573
574nub_addr_t
575MachTask::GetDYLDAllImageInfosAddress (DNBError& err)
576{
577 struct hack_task_dyld_info dyld_info;
578 mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
579 // Make sure that COUNT isn't bigger than our hacked up struct hack_task_dyld_info.
580 // If it is, then make COUNT smaller to match.
581 if (count > (sizeof(struct hack_task_dyld_info) / sizeof(natural_t)))
582 count = (sizeof(struct hack_task_dyld_info) / sizeof(natural_t));
583
584 task_t task = TaskPortForProcessID (err);
585 if (err.Success())
586 {
587 err = ::task_info (task, TASK_DYLD_INFO, (task_info_t)&dyld_info, &count);
588 if (err.Success())
589 {
590 // We now have the address of the all image infos structure
591 return dyld_info.all_image_info_addr;
592 }
593 }
594 return INVALID_NUB_ADDRESS;
595}
596
597
598//----------------------------------------------------------------------
599// MachTask::AllocateMemory
600//----------------------------------------------------------------------
601nub_addr_t
602MachTask::AllocateMemory (size_t size, uint32_t permissions)
603{
604 mach_vm_address_t addr;
605 task_t task = TaskPort();
606 if (task == TASK_NULL)
607 return INVALID_NUB_ADDRESS;
608
609 DNBError err;
610 err = ::mach_vm_allocate (task, &addr, size, TRUE);
611 if (err.Error() == KERN_SUCCESS)
612 {
613 // Set the protections:
614 vm_prot_t mach_prot = 0;
615 if (permissions & eMemoryPermissionsReadable)
616 mach_prot |= VM_PROT_READ;
617 if (permissions & eMemoryPermissionsWritable)
618 mach_prot |= VM_PROT_WRITE;
619 if (permissions & eMemoryPermissionsExecutable)
620 mach_prot |= VM_PROT_EXECUTE;
621
622
623 err = ::mach_vm_protect (task, addr, size, 0, mach_prot);
624 if (err.Error() == KERN_SUCCESS)
625 {
626 m_allocations.insert (std::make_pair(addr, size));
627 return addr;
628 }
629 ::mach_vm_deallocate (task, addr, size);
630 }
631 return INVALID_NUB_ADDRESS;
632}
633
634//----------------------------------------------------------------------
635// MachTask::DeallocateMemory
636//----------------------------------------------------------------------
637nub_bool_t
638MachTask::DeallocateMemory (nub_addr_t addr)
639{
640 task_t task = TaskPort();
641 if (task == TASK_NULL)
642 return false;
643
644 // We have to stash away sizes for the allocations...
645 allocation_collection::iterator pos, end = m_allocations.end();
646 for (pos = m_allocations.begin(); pos != end; pos++)
647 {
648 if ((*pos).first == addr)
649 {
650 m_allocations.erase(pos);
651 return ::mach_vm_deallocate (task, (*pos).first, (*pos).second) == KERN_SUCCESS;
652 }
653
654 }
655 return false;
656}
657