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