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