1 //===-- Genealogy.cpp -------------------------------------------*- C++ -*-===//
2 //
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
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include <Availability.h>
10 #include <dlfcn.h>
11 #include <string>
12 #include <uuid/uuid.h>
13
14 #include "DNBDefs.h"
15 #include "Genealogy.h"
16 #include "GenealogySPI.h"
17 #include "MachThreadList.h"
18
19 /// Constructor
20
Genealogy()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");
48 m_os_trace_get_type =
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");
61 }
62
63 Genealogy::ThreadActivitySP
GetGenealogyInfoForThread(pid_t pid,nub_thread_t tid,const MachThreadList & thread_list,task_t task,bool & timed_out)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;
68 //
69 // if we've timed out trying to get the activities, don't try again at this
70 // process stop.
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.
73
74 if (m_thread_activities.size() == 0 && !m_diagnosticd_call_timed_out) {
75 GetActivities(pid, thread_list, task);
76 }
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;
81 }
82 timed_out = m_diagnosticd_call_timed_out;
83 return activity;
84 }
85
Clear()86 void Genealogy::Clear() {
87 m_thread_activities.clear();
88 m_diagnosticd_call_timed_out = false;
89 }
90
GetActivities(pid_t pid,const MachThreadList & thread_list,task_t task)91 void Genealogy::GetActivities(pid_t pid, const MachThreadList &thread_list,
92 task_t task) {
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;
107
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) {
113 if (error == 0) {
114 m_os_activity_iterate_processes(processes, ^bool(
115 os_activity_process_t
116 process_info) {
117 if (pid == process_info->pid) {
118 // Collect all the Breadcrumbs
119 m_os_activity_iterate_breadcrumbs(
120 process_info,
121 ^bool(os_activity_breadcrumb_t breadcrumb) {
122 Breadcrumb bc;
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);
129 return true;
130 });
131
132 // Collect all the Activites
133 m_os_activity_iterate_activities(
134 process_info->activities, process_info,
135 ^bool(os_activity_entry_t activity) {
136 Activity ac;
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);
145 return true;
146 });
147
148 // Collect all the Messages -- messages not associated with
149 // any thread
150 m_os_activity_iterate_messages(
151 process_info->messages, process_info,
152 ^bool(os_trace_message_t trace_msg) {
153 Message 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);
158 msg.activity_id = 0;
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);
167 }
168 const char *message_text =
169 m_os_trace_copy_formatted_message(trace_msg);
170 if (message_text)
171 msg.message = message_text;
172 messages.push_back(msg);
173 return true;
174 });
175
176 // Discover which activities are said to be running on
177 // threads currently
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);
186 }
187 if (act != 0)
188 thread_activity_mapping[thread_id] = act;
189 }
190
191 // Collect all Messages -- messages associated with a thread
192
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,
207 thread_id);
208 m_os_activity_iterate_messages(
209 this_thread_messages, process_info,
210 ^bool(os_trace_message_t trace_msg) {
211 Message 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);
227 }
228 const char *message_text =
229 m_os_trace_copy_formatted_message(trace_msg);
230 if (message_text)
231 msg.message = message_text;
232 messages.push_back(msg);
233 return true;
234 });
235 }
236 }
237 }
238 return true;
239 });
240 }
241 dispatch_semaphore_signal(semaphore);
242 }) == true) {
243 // Wait for the diagnosticd xpc calls to all finish up -- or half a second
244 // to elapse.
245 dispatch_time_t timeout =
246 dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC / 2);
247 bool success = dispatch_semaphore_wait(semaphore, timeout) == 0;
248 if (!success) {
249 m_diagnosticd_call_timed_out = true;
250 return;
251 }
252 }
253
254 // breadcrumbs, activities, and messages have all now been filled in.
255
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;
267
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);
273 }
274 }
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);
280 }
281 }
282
283 m_thread_activities[thread_id] = thread_activity_sp;
284 break;
285 }
286 }
287 }
288 }
289 }
290
291 uint32_t
AddProcessExecutableInfo(ProcessExecutableInfoSP process_exe_info)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) {
298 return idx + 1;
299 }
300 }
301 m_process_executable_infos.push_back(process_exe_info);
302 return info_size + 1;
303 }
304
305 Genealogy::ProcessExecutableInfoSP
GetProcessExecutableInfosAtIndex(size_t idx)306 Genealogy::GetProcessExecutableInfosAtIndex(size_t idx) {
307 ProcessExecutableInfoSP info_sp;
308 if (idx > 0) {
309 idx--;
310 if (idx <= m_process_executable_infos.size()) {
311 info_sp = m_process_executable_infos[idx];
312 }
313 }
314 return info_sp;
315 }
316