316 lines · cpp
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-exception6//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/// Constructor20 21Genealogy::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 63Genealogy::ThreadActivitySP64Genealogy::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 this70 // 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 86void Genealogy::Clear() {87 m_thread_activities.clear();88 m_diagnosticd_call_timed_out = false;89}90 91void 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_t116 process_info) {117 if (pid == process_info->pid) {118 // Collect all the Breadcrumbs119 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 Activities133 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 with149 // any thread150 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 on177 // threads currently178 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 thread192 193 // When there's no genealogy information, an early version194 // of os_activity_messages_for_thread195 // can crash in rare circumstances. Check to see if this196 // process has any activities before197 // 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 second244 // 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 291uint32_t292Genealogy::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 305Genealogy::ProcessExecutableInfoSP306Genealogy::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