1 ///===-- Activity.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 #include <Availability.h>
11 #include <dlfcn.h>
12 #include <string>
13 #include <uuid/uuid.h>
14 
15 #include "DNBDefs.h"
16 #include "Genealogy.h"
17 #include "GenealogySPI.h"
18 #include "MachThreadList.h"
19 
20 //---------------------------
21 /// Constructor
22 //---------------------------
23 
24 Genealogy::Genealogy()
25     : m_os_activity_diagnostic_for_pid(nullptr),
26       m_os_activity_iterate_processes(nullptr),
27       m_os_activity_iterate_breadcrumbs(nullptr),
28       m_os_activity_iterate_messages(nullptr),
29       m_os_activity_iterate_activities(nullptr), m_os_trace_get_type(nullptr),
30       m_os_trace_copy_formatted_message(nullptr),
31       m_os_activity_for_thread(nullptr), m_os_activity_for_task_thread(nullptr),
32       m_thread_activities(), m_process_executable_infos(),
33       m_diagnosticd_call_timed_out(false) {
34   m_os_activity_diagnostic_for_pid =
35       (bool (*)(pid_t, os_activity_t, uint32_t, os_diagnostic_block_t))dlsym(
36           RTLD_DEFAULT, "os_activity_diagnostic_for_pid");
37   m_os_activity_iterate_processes =
38       (void (*)(os_activity_process_list_t, bool (^)(os_activity_process_t)))
39           dlsym(RTLD_DEFAULT, "os_activity_iterate_processes");
40   m_os_activity_iterate_breadcrumbs =
41       (void (*)(os_activity_process_t, bool (^)(os_activity_breadcrumb_t)))
42           dlsym(RTLD_DEFAULT, "os_activity_iterate_breadcrumbs");
43   m_os_activity_iterate_messages = (void (*)(
44       os_trace_message_list_t, os_activity_process_t,
45       bool (^)(os_trace_message_t)))dlsym(RTLD_DEFAULT,
46                                           "os_activity_iterate_messages");
47   m_os_activity_iterate_activities = (void (*)(
48       os_activity_list_t, os_activity_process_t,
49       bool (^)(os_activity_entry_t)))dlsym(RTLD_DEFAULT,
50                                            "os_activity_iterate_activities");
51   m_os_trace_get_type =
52       (uint8_t(*)(os_trace_message_t))dlsym(RTLD_DEFAULT, "os_trace_get_type");
53   m_os_trace_copy_formatted_message = (char *(*)(os_trace_message_t))dlsym(
54       RTLD_DEFAULT, "os_trace_copy_formatted_message");
55   m_os_activity_for_thread =
56       (os_activity_t(*)(os_activity_process_t, uint64_t))dlsym(
57           RTLD_DEFAULT, "os_activity_for_thread");
58   m_os_activity_for_task_thread = (os_activity_t(*)(task_t, uint64_t))dlsym(
59       RTLD_DEFAULT, "os_activity_for_task_thread");
60   m_os_activity_messages_for_thread = (os_trace_message_list_t(*)(
61       os_activity_process_t process, os_activity_t activity,
62       uint64_t thread_id))dlsym(RTLD_DEFAULT,
63                                 "os_activity_messages_for_thread");
64 }
65 
66 Genealogy::ThreadActivitySP
67 Genealogy::GetGenealogyInfoForThread(pid_t pid, nub_thread_t tid,
68                                      const MachThreadList &thread_list,
69                                      task_t task, bool &timed_out) {
70   ThreadActivitySP activity;
71   //
72   // if we've timed out trying to get the activities, don't try again at this
73   // process stop.
74   // (else we'll need to hit the timeout for every thread we're asked about.)
75   // We'll try again at the next public stop.
76 
77   if (m_thread_activities.size() == 0 && !m_diagnosticd_call_timed_out) {
78     GetActivities(pid, thread_list, task);
79   }
80   std::map<nub_thread_t, ThreadActivitySP>::const_iterator search;
81   search = m_thread_activities.find(tid);
82   if (search != m_thread_activities.end()) {
83     activity = search->second;
84   }
85   timed_out = m_diagnosticd_call_timed_out;
86   return activity;
87 }
88 
89 void Genealogy::Clear() {
90   m_thread_activities.clear();
91   m_diagnosticd_call_timed_out = false;
92 }
93 
94 void Genealogy::GetActivities(pid_t pid, const MachThreadList &thread_list,
95                               task_t task) {
96   if (m_os_activity_diagnostic_for_pid != nullptr &&
97       m_os_activity_iterate_processes != nullptr &&
98       m_os_activity_iterate_breadcrumbs != nullptr &&
99       m_os_activity_iterate_messages != nullptr &&
100       m_os_activity_iterate_activities != nullptr &&
101       m_os_trace_get_type != nullptr &&
102       m_os_trace_copy_formatted_message != nullptr &&
103       (m_os_activity_for_thread != nullptr ||
104        m_os_activity_for_task_thread != nullptr)) {
105     __block dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
106     __block BreadcrumbList breadcrumbs;
107     __block ActivityList activities;
108     __block MessageList messages;
109     __block std::map<nub_thread_t, uint64_t> thread_activity_mapping;
110 
111     os_activity_diagnostic_flag_t flags =
112         OS_ACTIVITY_DIAGNOSTIC_ALL_ACTIVITIES |
113         OS_ACTIVITY_DIAGNOSTIC_PROCESS_ONLY;
114     if (m_os_activity_diagnostic_for_pid(
115             pid, 0, flags, ^(os_activity_process_list_t processes, int error) {
116               if (error == 0) {
117                 m_os_activity_iterate_processes(processes, ^bool(
118                                                     os_activity_process_t
119                                                         process_info) {
120                   if (pid == process_info->pid) {
121                     // Collect all the Breadcrumbs
122                     m_os_activity_iterate_breadcrumbs(
123                         process_info,
124                         ^bool(os_activity_breadcrumb_t breadcrumb) {
125                           Breadcrumb bc;
126                           bc.breadcrumb_id = breadcrumb->breadcrumb_id;
127                           bc.activity_id = breadcrumb->activity_id;
128                           bc.timestamp = breadcrumb->timestamp;
129                           if (breadcrumb->name)
130                             bc.name = breadcrumb->name;
131                           breadcrumbs.push_back(bc);
132                           return true;
133                         });
134 
135                     // Collect all the Activites
136                     m_os_activity_iterate_activities(
137                         process_info->activities, process_info,
138                         ^bool(os_activity_entry_t activity) {
139                           Activity ac;
140                           ac.activity_start = activity->activity_start;
141                           ac.activity_id = activity->activity_id;
142                           ac.parent_id = activity->parent_id;
143                           if (activity->activity_name)
144                             ac.activity_name = activity->activity_name;
145                           if (activity->reason)
146                             ac.reason = activity->reason;
147                           activities.push_back(ac);
148                           return true;
149                         });
150 
151                     // Collect all the Messages -- messages not associated with
152                     // any thread
153                     m_os_activity_iterate_messages(
154                         process_info->messages, process_info,
155                         ^bool(os_trace_message_t trace_msg) {
156                           Message msg;
157                           msg.timestamp = trace_msg->timestamp;
158                           msg.trace_id = trace_msg->trace_id;
159                           msg.thread = trace_msg->thread;
160                           msg.type = m_os_trace_get_type(trace_msg);
161                           msg.activity_id = 0;
162                           if (trace_msg->image_uuid && trace_msg->image_path) {
163                             ProcessExecutableInfoSP process_info_sp(
164                                 new ProcessExecutableInfo());
165                             uuid_copy(process_info_sp->image_uuid,
166                                       trace_msg->image_uuid);
167                             process_info_sp->image_path = trace_msg->image_path;
168                             msg.process_info_index =
169                                 AddProcessExecutableInfo(process_info_sp);
170                           }
171                           const char *message_text =
172                               m_os_trace_copy_formatted_message(trace_msg);
173                           if (message_text)
174                             msg.message = message_text;
175                           messages.push_back(msg);
176                           return true;
177                         });
178 
179                     // Discover which activities are said to be running on
180                     // threads currently
181                     const nub_size_t num_threads = thread_list.NumThreads();
182                     for (nub_size_t i = 0; i < num_threads; ++i) {
183                       nub_thread_t thread_id = thread_list.ThreadIDAtIndex(i);
184                       os_activity_t act = 0;
185                       if (m_os_activity_for_task_thread != nullptr) {
186                         act = m_os_activity_for_task_thread(task, thread_id);
187                       } else if (m_os_activity_for_thread != nullptr) {
188                         act = m_os_activity_for_thread(process_info, thread_id);
189                       }
190                       if (act != 0)
191                         thread_activity_mapping[thread_id] = act;
192                     }
193 
194                     // Collect all Messages -- messages associated with a thread
195 
196                     // When there's no genealogy information, an early version
197                     // of os_activity_messages_for_thread
198                     // can crash in rare circumstances.  Check to see if this
199                     // process has any activities before
200                     // making the call to get messages.
201                     if (process_info->activities != nullptr &&
202                         thread_activity_mapping.size() > 0) {
203                       std::map<nub_thread_t, uint64_t>::const_iterator iter;
204                       for (iter = thread_activity_mapping.begin();
205                            iter != thread_activity_mapping.end(); ++iter) {
206                         nub_thread_t thread_id = iter->first;
207                         os_activity_t act = iter->second;
208                         os_trace_message_list_t this_thread_messages =
209                             m_os_activity_messages_for_thread(process_info, act,
210                                                               thread_id);
211                         m_os_activity_iterate_messages(
212                             this_thread_messages, process_info,
213                             ^bool(os_trace_message_t trace_msg) {
214                               Message msg;
215                               msg.timestamp = trace_msg->timestamp;
216                               msg.trace_id = trace_msg->trace_id;
217                               msg.thread = trace_msg->thread;
218                               msg.type = m_os_trace_get_type(trace_msg);
219                               msg.activity_id = act;
220                               if (trace_msg->image_uuid &&
221                                   trace_msg->image_path) {
222                                 ProcessExecutableInfoSP process_info_sp(
223                                     new ProcessExecutableInfo());
224                                 uuid_copy(process_info_sp->image_uuid,
225                                           trace_msg->image_uuid);
226                                 process_info_sp->image_path =
227                                     trace_msg->image_path;
228                                 msg.process_info_index =
229                                     AddProcessExecutableInfo(process_info_sp);
230                               }
231                               const char *message_text =
232                                   m_os_trace_copy_formatted_message(trace_msg);
233                               if (message_text)
234                                 msg.message = message_text;
235                               messages.push_back(msg);
236                               return true;
237                             });
238                       }
239                     }
240                   }
241                   return true;
242                 });
243               }
244               dispatch_semaphore_signal(semaphore);
245             }) == true) {
246       // Wait for the diagnosticd xpc calls to all finish up -- or half a second
247       // to elapse.
248       dispatch_time_t timeout =
249           dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC / 2);
250       bool success = dispatch_semaphore_wait(semaphore, timeout) == 0;
251       if (!success) {
252         m_diagnosticd_call_timed_out = true;
253         return;
254       }
255     }
256 
257     // breadcrumbs, activities, and messages have all now been filled in.
258 
259     std::map<nub_thread_t, uint64_t>::const_iterator iter;
260     for (iter = thread_activity_mapping.begin();
261          iter != thread_activity_mapping.end(); ++iter) {
262       nub_thread_t thread_id = iter->first;
263       uint64_t activity_id = iter->second;
264       ActivityList::const_iterator activity_search;
265       for (activity_search = activities.begin();
266            activity_search != activities.end(); ++activity_search) {
267         if (activity_search->activity_id == activity_id) {
268           ThreadActivitySP thread_activity_sp(new ThreadActivity());
269           thread_activity_sp->current_activity = *activity_search;
270 
271           BreadcrumbList::const_iterator breadcrumb_search;
272           for (breadcrumb_search = breadcrumbs.begin();
273                breadcrumb_search != breadcrumbs.end(); ++breadcrumb_search) {
274             if (breadcrumb_search->activity_id == activity_id) {
275               thread_activity_sp->breadcrumbs.push_back(*breadcrumb_search);
276             }
277           }
278           MessageList::const_iterator message_search;
279           for (message_search = messages.begin();
280                message_search != messages.end(); ++message_search) {
281             if (message_search->thread == thread_id) {
282               thread_activity_sp->messages.push_back(*message_search);
283             }
284           }
285 
286           m_thread_activities[thread_id] = thread_activity_sp;
287           break;
288         }
289       }
290     }
291   }
292 }
293 
294 uint32_t
295 Genealogy::AddProcessExecutableInfo(ProcessExecutableInfoSP process_exe_info) {
296   const uint32_t info_size =
297       static_cast<uint32_t>(m_process_executable_infos.size());
298   for (uint32_t idx = 0; idx < info_size; ++idx) {
299     if (uuid_compare(m_process_executable_infos[idx]->image_uuid,
300                      process_exe_info->image_uuid) == 0) {
301       return idx + 1;
302     }
303   }
304   m_process_executable_infos.push_back(process_exe_info);
305   return info_size + 1;
306 }
307 
308 Genealogy::ProcessExecutableInfoSP
309 Genealogy::GetProcessExecutableInfosAtIndex(size_t idx) {
310   ProcessExecutableInfoSP info_sp;
311   if (idx > 0) {
312     idx--;
313     if (idx <= m_process_executable_infos.size()) {
314       info_sp = m_process_executable_infos[idx];
315     }
316   }
317   return info_sp;
318 }
319