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