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