1 //===-- Genealogy.cpp -------------------------------------------*- C++ -*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include <Availability.h>
12 #include <uuid/uuid.h>
15 #include "Genealogy.h"
16 #include "GenealogySPI.h"
17 #include "MachThreadList.h"
21 Genealogy::Genealogy()
22 : m_os_activity_diagnostic_for_pid(nullptr),
23 m_os_activity_iterate_processes(nullptr),
24 m_os_activity_iterate_breadcrumbs(nullptr),
25 m_os_activity_iterate_messages(nullptr),
26 m_os_activity_iterate_activities(nullptr), m_os_trace_get_type(nullptr),
27 m_os_trace_copy_formatted_message(nullptr),
28 m_os_activity_for_thread(nullptr), m_os_activity_for_task_thread(nullptr),
29 m_thread_activities(), m_process_executable_infos(),
30 m_diagnosticd_call_timed_out(false) {
31 m_os_activity_diagnostic_for_pid
=
32 (bool (*)(pid_t
, os_activity_t
, uint32_t, os_diagnostic_block_t
))dlsym(
33 RTLD_DEFAULT
, "os_activity_diagnostic_for_pid");
34 m_os_activity_iterate_processes
=
35 (void (*)(os_activity_process_list_t
, bool (^)(os_activity_process_t
)))
36 dlsym(RTLD_DEFAULT
, "os_activity_iterate_processes");
37 m_os_activity_iterate_breadcrumbs
=
38 (void (*)(os_activity_process_t
, bool (^)(os_activity_breadcrumb_t
)))
39 dlsym(RTLD_DEFAULT
, "os_activity_iterate_breadcrumbs");
40 m_os_activity_iterate_messages
= (void (*)(
41 os_trace_message_list_t
, os_activity_process_t
,
42 bool (^)(os_trace_message_t
)))dlsym(RTLD_DEFAULT
,
43 "os_activity_iterate_messages");
44 m_os_activity_iterate_activities
= (void (*)(
45 os_activity_list_t
, os_activity_process_t
,
46 bool (^)(os_activity_entry_t
)))dlsym(RTLD_DEFAULT
,
47 "os_activity_iterate_activities");
49 (uint8_t(*)(os_trace_message_t
))dlsym(RTLD_DEFAULT
, "os_trace_get_type");
50 m_os_trace_copy_formatted_message
= (char *(*)(os_trace_message_t
))dlsym(
51 RTLD_DEFAULT
, "os_trace_copy_formatted_message");
52 m_os_activity_for_thread
=
53 (os_activity_t(*)(os_activity_process_t
, uint64_t))dlsym(
54 RTLD_DEFAULT
, "os_activity_for_thread");
55 m_os_activity_for_task_thread
= (os_activity_t(*)(task_t
, uint64_t))dlsym(
56 RTLD_DEFAULT
, "os_activity_for_task_thread");
57 m_os_activity_messages_for_thread
= (os_trace_message_list_t(*)(
58 os_activity_process_t process
, os_activity_t activity
,
59 uint64_t thread_id
))dlsym(RTLD_DEFAULT
,
60 "os_activity_messages_for_thread");
63 Genealogy::ThreadActivitySP
64 Genealogy::GetGenealogyInfoForThread(pid_t pid
, nub_thread_t tid
,
65 const MachThreadList
&thread_list
,
66 task_t task
, bool &timed_out
) {
67 ThreadActivitySP activity
;
69 // if we've timed out trying to get the activities, don't try again at this
71 // (else we'll need to hit the timeout for every thread we're asked about.)
72 // We'll try again at the next public stop.
74 if (m_thread_activities
.size() == 0 && !m_diagnosticd_call_timed_out
) {
75 GetActivities(pid
, thread_list
, task
);
77 std::map
<nub_thread_t
, ThreadActivitySP
>::const_iterator search
;
78 search
= m_thread_activities
.find(tid
);
79 if (search
!= m_thread_activities
.end()) {
80 activity
= search
->second
;
82 timed_out
= m_diagnosticd_call_timed_out
;
86 void Genealogy::Clear() {
87 m_thread_activities
.clear();
88 m_diagnosticd_call_timed_out
= false;
91 void Genealogy::GetActivities(pid_t pid
, const MachThreadList
&thread_list
,
93 if (m_os_activity_diagnostic_for_pid
!= nullptr &&
94 m_os_activity_iterate_processes
!= nullptr &&
95 m_os_activity_iterate_breadcrumbs
!= nullptr &&
96 m_os_activity_iterate_messages
!= nullptr &&
97 m_os_activity_iterate_activities
!= nullptr &&
98 m_os_trace_get_type
!= nullptr &&
99 m_os_trace_copy_formatted_message
!= nullptr &&
100 (m_os_activity_for_thread
!= nullptr ||
101 m_os_activity_for_task_thread
!= nullptr)) {
102 __block dispatch_semaphore_t semaphore
= dispatch_semaphore_create(0);
103 __block BreadcrumbList breadcrumbs
;
104 __block ActivityList activities
;
105 __block MessageList messages
;
106 __block
std::map
<nub_thread_t
, uint64_t> thread_activity_mapping
;
108 os_activity_diagnostic_flag_t flags
=
109 OS_ACTIVITY_DIAGNOSTIC_ALL_ACTIVITIES
|
110 OS_ACTIVITY_DIAGNOSTIC_PROCESS_ONLY
;
111 if (m_os_activity_diagnostic_for_pid(
112 pid
, 0, flags
, ^(os_activity_process_list_t processes
, int error
) {
114 m_os_activity_iterate_processes(processes
, ^bool(
115 os_activity_process_t
117 if (pid
== process_info
->pid
) {
118 // Collect all the Breadcrumbs
119 m_os_activity_iterate_breadcrumbs(
121 ^bool(os_activity_breadcrumb_t breadcrumb
) {
123 bc
.breadcrumb_id
= breadcrumb
->breadcrumb_id
;
124 bc
.activity_id
= breadcrumb
->activity_id
;
125 bc
.timestamp
= breadcrumb
->timestamp
;
126 if (breadcrumb
->name
)
127 bc
.name
= breadcrumb
->name
;
128 breadcrumbs
.push_back(bc
);
132 // Collect all the Activites
133 m_os_activity_iterate_activities(
134 process_info
->activities
, process_info
,
135 ^bool(os_activity_entry_t activity
) {
137 ac
.activity_start
= activity
->activity_start
;
138 ac
.activity_id
= activity
->activity_id
;
139 ac
.parent_id
= activity
->parent_id
;
140 if (activity
->activity_name
)
141 ac
.activity_name
= activity
->activity_name
;
142 if (activity
->reason
)
143 ac
.reason
= activity
->reason
;
144 activities
.push_back(ac
);
148 // Collect all the Messages -- messages not associated with
150 m_os_activity_iterate_messages(
151 process_info
->messages
, process_info
,
152 ^bool(os_trace_message_t trace_msg
) {
154 msg
.timestamp
= trace_msg
->timestamp
;
155 msg
.trace_id
= trace_msg
->trace_id
;
156 msg
.thread
= trace_msg
->thread
;
157 msg
.type
= m_os_trace_get_type(trace_msg
);
159 if (trace_msg
->image_uuid
&& trace_msg
->image_path
) {
160 ProcessExecutableInfoSP
process_info_sp(
161 new ProcessExecutableInfo());
162 uuid_copy(process_info_sp
->image_uuid
,
163 trace_msg
->image_uuid
);
164 process_info_sp
->image_path
= trace_msg
->image_path
;
165 msg
.process_info_index
=
166 AddProcessExecutableInfo(process_info_sp
);
168 const char *message_text
=
169 m_os_trace_copy_formatted_message(trace_msg
);
171 msg
.message
= message_text
;
172 messages
.push_back(msg
);
176 // Discover which activities are said to be running on
178 const nub_size_t num_threads
= thread_list
.NumThreads();
179 for (nub_size_t i
= 0; i
< num_threads
; ++i
) {
180 nub_thread_t thread_id
= thread_list
.ThreadIDAtIndex(i
);
181 os_activity_t act
= 0;
182 if (m_os_activity_for_task_thread
!= nullptr) {
183 act
= m_os_activity_for_task_thread(task
, thread_id
);
184 } else if (m_os_activity_for_thread
!= nullptr) {
185 act
= m_os_activity_for_thread(process_info
, thread_id
);
188 thread_activity_mapping
[thread_id
] = act
;
191 // Collect all Messages -- messages associated with a thread
193 // When there's no genealogy information, an early version
194 // of os_activity_messages_for_thread
195 // can crash in rare circumstances. Check to see if this
196 // process has any activities before
197 // making the call to get messages.
198 if (process_info
->activities
!= nullptr &&
199 thread_activity_mapping
.size() > 0) {
200 std::map
<nub_thread_t
, uint64_t>::const_iterator iter
;
201 for (iter
= thread_activity_mapping
.begin();
202 iter
!= thread_activity_mapping
.end(); ++iter
) {
203 nub_thread_t thread_id
= iter
->first
;
204 os_activity_t act
= iter
->second
;
205 os_trace_message_list_t this_thread_messages
=
206 m_os_activity_messages_for_thread(process_info
, act
,
208 m_os_activity_iterate_messages(
209 this_thread_messages
, process_info
,
210 ^bool(os_trace_message_t trace_msg
) {
212 msg
.timestamp
= trace_msg
->timestamp
;
213 msg
.trace_id
= trace_msg
->trace_id
;
214 msg
.thread
= trace_msg
->thread
;
215 msg
.type
= m_os_trace_get_type(trace_msg
);
216 msg
.activity_id
= act
;
217 if (trace_msg
->image_uuid
&&
218 trace_msg
->image_path
) {
219 ProcessExecutableInfoSP
process_info_sp(
220 new ProcessExecutableInfo());
221 uuid_copy(process_info_sp
->image_uuid
,
222 trace_msg
->image_uuid
);
223 process_info_sp
->image_path
=
224 trace_msg
->image_path
;
225 msg
.process_info_index
=
226 AddProcessExecutableInfo(process_info_sp
);
228 const char *message_text
=
229 m_os_trace_copy_formatted_message(trace_msg
);
231 msg
.message
= message_text
;
232 messages
.push_back(msg
);
241 dispatch_semaphore_signal(semaphore
);
243 // Wait for the diagnosticd xpc calls to all finish up -- or half a second
245 dispatch_time_t timeout
=
246 dispatch_time(DISPATCH_TIME_NOW
, NSEC_PER_SEC
/ 2);
247 bool success
= dispatch_semaphore_wait(semaphore
, timeout
) == 0;
249 m_diagnosticd_call_timed_out
= true;
254 // breadcrumbs, activities, and messages have all now been filled in.
256 std::map
<nub_thread_t
, uint64_t>::const_iterator iter
;
257 for (iter
= thread_activity_mapping
.begin();
258 iter
!= thread_activity_mapping
.end(); ++iter
) {
259 nub_thread_t thread_id
= iter
->first
;
260 uint64_t activity_id
= iter
->second
;
261 ActivityList::const_iterator activity_search
;
262 for (activity_search
= activities
.begin();
263 activity_search
!= activities
.end(); ++activity_search
) {
264 if (activity_search
->activity_id
== activity_id
) {
265 ThreadActivitySP
thread_activity_sp(new ThreadActivity());
266 thread_activity_sp
->current_activity
= *activity_search
;
268 BreadcrumbList::const_iterator breadcrumb_search
;
269 for (breadcrumb_search
= breadcrumbs
.begin();
270 breadcrumb_search
!= breadcrumbs
.end(); ++breadcrumb_search
) {
271 if (breadcrumb_search
->activity_id
== activity_id
) {
272 thread_activity_sp
->breadcrumbs
.push_back(*breadcrumb_search
);
275 MessageList::const_iterator message_search
;
276 for (message_search
= messages
.begin();
277 message_search
!= messages
.end(); ++message_search
) {
278 if (message_search
->thread
== thread_id
) {
279 thread_activity_sp
->messages
.push_back(*message_search
);
283 m_thread_activities
[thread_id
] = thread_activity_sp
;
292 Genealogy::AddProcessExecutableInfo(ProcessExecutableInfoSP process_exe_info
) {
293 const uint32_t info_size
=
294 static_cast<uint32_t>(m_process_executable_infos
.size());
295 for (uint32_t idx
= 0; idx
< info_size
; ++idx
) {
296 if (uuid_compare(m_process_executable_infos
[idx
]->image_uuid
,
297 process_exe_info
->image_uuid
) == 0) {
301 m_process_executable_infos
.push_back(process_exe_info
);
302 return info_size
+ 1;
305 Genealogy::ProcessExecutableInfoSP
306 Genealogy::GetProcessExecutableInfosAtIndex(size_t idx
) {
307 ProcessExecutableInfoSP info_sp
;
310 if (idx
<= m_process_executable_infos
.size()) {
311 info_sp
= m_process_executable_infos
[idx
];