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