1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
2 | // for details. All rights reserved. Use of this source code is governed by a |
3 | // BSD-style license that can be found in the LICENSE file. |
4 | |
5 | #include "platform/globals.h" |
6 | #if defined(HOST_OS_MACOS) |
7 | |
8 | #include <assert.h> // NOLINT |
9 | #include <errno.h> // NOLINT |
10 | #include <stdbool.h> // NOLINT |
11 | #include <sys/sysctl.h> // NOLINT |
12 | #include <sys/types.h> // NOLINT |
13 | #include <unistd.h> // NOLINT |
14 | |
15 | #include "vm/flags.h" |
16 | #include "vm/os.h" |
17 | #include "vm/profiler.h" |
18 | #include "vm/signal_handler.h" |
19 | #include "vm/thread_interrupter.h" |
20 | |
21 | namespace dart { |
22 | |
23 | #ifndef PRODUCT |
24 | |
25 | DECLARE_FLAG(bool, trace_thread_interrupter); |
26 | |
27 | // Returns true if the current process is being debugged (either |
28 | // running under the debugger or has a debugger attached post facto). |
29 | // Code from https://developer.apple.com/library/content/qa/qa1361/_index.html |
30 | bool ThreadInterrupter::IsDebuggerAttached() { |
31 | struct kinfo_proc info; |
32 | // Initialize the flags so that, if sysctl fails for some bizarre |
33 | // reason, we get a predictable result. |
34 | info.kp_proc.p_flag = 0; |
35 | // Initialize mib, which tells sysctl the info we want, in this case |
36 | // we're looking for information about a specific process ID. |
37 | int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()}; |
38 | size_t size = sizeof(info); |
39 | |
40 | // Call sysctl. |
41 | size = sizeof(info); |
42 | int junk = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0); |
43 | ASSERT(junk == 0); |
44 | // We're being debugged if the P_TRACED flag is set. |
45 | return ((info.kp_proc.p_flag & P_TRACED) != 0); |
46 | } |
47 | |
48 | class ThreadInterrupterMacOS : public AllStatic { |
49 | public: |
50 | static void ThreadInterruptSignalHandler(int signal, |
51 | siginfo_t* info, |
52 | void* context_) { |
53 | if (signal != SIGPROF) { |
54 | return; |
55 | } |
56 | Thread* thread = Thread::Current(); |
57 | if (thread == NULL) { |
58 | return; |
59 | } |
60 | ThreadInterrupter::SampleBufferWriterScope scope; |
61 | if (!scope.CanSample()) { |
62 | return; |
63 | } |
64 | // Extract thread state. |
65 | ucontext_t* context = reinterpret_cast<ucontext_t*>(context_); |
66 | mcontext_t mcontext = context->uc_mcontext; |
67 | InterruptedThreadState its; |
68 | its.pc = SignalHandler::GetProgramCounter(mcontext); |
69 | its.fp = SignalHandler::GetFramePointer(mcontext); |
70 | its.csp = SignalHandler::GetCStackPointer(mcontext); |
71 | its.dsp = SignalHandler::GetDartStackPointer(mcontext); |
72 | its.lr = SignalHandler::GetLinkRegister(mcontext); |
73 | Profiler::SampleThread(thread, its); |
74 | } |
75 | }; |
76 | |
77 | void ThreadInterrupter::InterruptThread(OSThread* thread) { |
78 | if (FLAG_trace_thread_interrupter) { |
79 | OS::PrintErr("ThreadInterrupter interrupting %p\n" , thread->id()); |
80 | } |
81 | int result = pthread_kill(thread->id(), SIGPROF); |
82 | ASSERT((result == 0) || (result == ESRCH)); |
83 | } |
84 | |
85 | void ThreadInterrupter::InstallSignalHandler() { |
86 | SignalHandler::Install< |
87 | ThreadInterrupterMacOS::ThreadInterruptSignalHandler>(); |
88 | } |
89 | |
90 | void ThreadInterrupter::RemoveSignalHandler() { |
91 | SignalHandler::Remove(); |
92 | } |
93 | |
94 | #endif // !PRODUCT |
95 | |
96 | } // namespace dart |
97 | |
98 | #endif // defined(HOST_OS_MACOS) |
99 | |