|
|
@@ -0,0 +1,302 @@ |
|
|
///===-- Activity.cpp ---------------------------------------*- C++ -*-===// |
|
|
// |
|
|
// The LLVM Compiler Infrastructure |
|
|
// |
|
|
// This file is distributed under the University of Illinois Open Source |
|
|
// License. See LICENSE.TXT for details. |
|
|
// |
|
|
//===----------------------------------------------------------------------===// |
|
|
|
|
|
#include <Availability.h> |
|
|
#include <string> |
|
|
#include <dlfcn.h> |
|
|
#include <uuid/uuid.h> |
|
|
|
|
|
#include "DNBDefs.h" |
|
|
#include "Genealogy.h" |
|
|
#include "GenealogySPI.h" |
|
|
#include "MachThreadList.h" |
|
|
|
|
|
//--------------------------- |
|
|
/// Constructor |
|
|
//--------------------------- |
|
|
|
|
|
Genealogy::Genealogy () : |
|
|
m_os_activity_diagnostic_for_pid (nullptr), |
|
|
m_os_activity_iterate_processes (nullptr), |
|
|
m_os_activity_iterate_breadcrumbs (nullptr), |
|
|
m_os_activity_iterate_messages (nullptr), |
|
|
m_os_activity_iterate_activities (nullptr), |
|
|
m_os_trace_get_type (nullptr), |
|
|
m_os_trace_copy_formatted_message (nullptr), |
|
|
m_os_activity_for_thread (nullptr), |
|
|
m_os_activity_for_task_thread (nullptr), |
|
|
m_thread_activities(), |
|
|
m_process_executable_infos(), |
|
|
m_diagnosticd_call_timed_out(false) |
|
|
{ |
|
|
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"); |
|
|
m_os_activity_iterate_processes = (void (*)(os_activity_process_list_t, bool (^)(os_activity_process_t)))dlsym (RTLD_DEFAULT, "os_activity_iterate_processes"); |
|
|
m_os_activity_iterate_breadcrumbs = (void (*)(os_activity_process_t, bool (^)(os_activity_breadcrumb_t))) dlsym (RTLD_DEFAULT, "os_activity_iterate_breadcrumbs"); |
|
|
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"); |
|
|
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"); |
|
|
m_os_trace_get_type = (uint8_t (*)(os_trace_message_t)) dlsym (RTLD_DEFAULT, "os_trace_get_type"); |
|
|
m_os_trace_copy_formatted_message = (char *(*)(os_trace_message_t)) dlsym (RTLD_DEFAULT, "os_trace_copy_formatted_message"); |
|
|
m_os_activity_for_thread = (os_activity_t (*)(os_activity_process_t, uint64_t)) dlsym (RTLD_DEFAULT, "os_activity_for_thread"); |
|
|
m_os_activity_for_task_thread = (os_activity_t (*)(task_t, uint64_t)) dlsym (RTLD_DEFAULT, "os_activity_for_task_thread"); |
|
|
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"); |
|
|
} |
|
|
|
|
|
Genealogy::ThreadActivitySP |
|
|
Genealogy::GetGenealogyInfoForThread (pid_t pid, nub_thread_t tid, const MachThreadList &thread_list, task_t task, bool &timed_out) |
|
|
{ |
|
|
ThreadActivitySP activity; |
|
|
// |
|
|
// if we've timed out trying to get the activities, don't try again at this process stop. |
|
|
// (else we'll need to hit the timeout for every thread we're asked about.) |
|
|
// We'll try again at the next public stop. |
|
|
|
|
|
if (m_thread_activities.size() == 0 && m_diagnosticd_call_timed_out == false) |
|
|
{ |
|
|
GetActivities(pid, thread_list, task); |
|
|
} |
|
|
std::map<nub_thread_t, ThreadActivitySP>::const_iterator search; |
|
|
search = m_thread_activities.find(tid); |
|
|
if (search != m_thread_activities.end()) |
|
|
{ |
|
|
activity = search->second; |
|
|
} |
|
|
timed_out = m_diagnosticd_call_timed_out; |
|
|
return activity; |
|
|
} |
|
|
|
|
|
void |
|
|
Genealogy::Clear() |
|
|
{ |
|
|
m_thread_activities.clear(); |
|
|
m_diagnosticd_call_timed_out = false; |
|
|
} |
|
|
|
|
|
void |
|
|
Genealogy::GetActivities(pid_t pid, const MachThreadList &thread_list, task_t task) |
|
|
{ |
|
|
if (m_os_activity_diagnostic_for_pid != nullptr |
|
|
&& m_os_activity_iterate_processes != nullptr |
|
|
&& m_os_activity_iterate_breadcrumbs != nullptr |
|
|
&& m_os_activity_iterate_messages != nullptr |
|
|
&& m_os_activity_iterate_activities != nullptr |
|
|
&& m_os_trace_get_type != nullptr |
|
|
&& m_os_trace_copy_formatted_message != nullptr |
|
|
&& (m_os_activity_for_thread != nullptr || m_os_activity_for_task_thread != nullptr) |
|
|
) |
|
|
{ |
|
|
__block dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); |
|
|
__block BreadcrumbList breadcrumbs; |
|
|
__block ActivityList activities; |
|
|
__block MessageList messages; |
|
|
__block std::map<nub_thread_t, uint64_t> thread_activity_mapping; |
|
|
|
|
|
os_activity_diagnostic_flag_t flags = OS_ACTIVITY_DIAGNOSTIC_ALL_ACTIVITIES | OS_ACTIVITY_DIAGNOSTIC_PROCESS_ONLY; |
|
|
if (m_os_activity_diagnostic_for_pid (pid, 0, flags, ^(os_activity_process_list_t processes, int error) |
|
|
{ |
|
|
if (error == 0) |
|
|
{ |
|
|
m_os_activity_iterate_processes (processes, ^bool(os_activity_process_t process_info) |
|
|
{ |
|
|
if (pid == process_info->pid) |
|
|
{ |
|
|
// Collect all the Breadcrumbs |
|
|
m_os_activity_iterate_breadcrumbs (process_info, ^bool(os_activity_breadcrumb_t breadcrumb) |
|
|
{ |
|
|
Breadcrumb bc; |
|
|
bc.breadcrumb_id = breadcrumb->breadcrumb_id; |
|
|
bc.activity_id = breadcrumb->activity_id; |
|
|
bc.timestamp = breadcrumb->timestamp; |
|
|
if (breadcrumb->name) |
|
|
bc.name = breadcrumb->name; |
|
|
breadcrumbs.push_back (bc); |
|
|
return true; |
|
|
}); |
|
|
|
|
|
// Collect all the Activites |
|
|
m_os_activity_iterate_activities (process_info->activities, process_info, ^bool(os_activity_entry_t activity) |
|
|
{ |
|
|
Activity ac; |
|
|
ac.activity_start = activity->activity_start; |
|
|
ac.activity_id = activity->activity_id; |
|
|
ac.parent_id = activity->parent_id; |
|
|
if (activity->activity_name) |
|
|
ac.activity_name = activity->activity_name; |
|
|
if (activity->reason) |
|
|
ac.reason = activity->reason; |
|
|
activities.push_back (ac); |
|
|
return true; |
|
|
}); |
|
|
|
|
|
|
|
|
// Collect all the Messages -- messages not associated with any thread |
|
|
m_os_activity_iterate_messages (process_info->messages, process_info, ^bool(os_trace_message_t trace_msg) |
|
|
{ |
|
|
Message msg; |
|
|
msg.timestamp = trace_msg->timestamp; |
|
|
msg.trace_id = trace_msg->trace_id; |
|
|
msg.thread = trace_msg->thread; |
|
|
msg.type = m_os_trace_get_type (trace_msg); |
|
|
msg.activity_id = 0; |
|
|
if (trace_msg->image_uuid && trace_msg->image_path) |
|
|
{ |
|
|
ProcessExecutableInfoSP process_info_sp (new ProcessExecutableInfo()); |
|
|
uuid_copy (process_info_sp->image_uuid, trace_msg->image_uuid); |
|
|
process_info_sp->image_path = trace_msg->image_path; |
|
|
msg.process_info_index = AddProcessExecutableInfo (process_info_sp); |
|
|
} |
|
|
const char *message_text = m_os_trace_copy_formatted_message (trace_msg); |
|
|
if (message_text) |
|
|
msg.message = message_text; |
|
|
messages.push_back (msg); |
|
|
return true; |
|
|
}); |
|
|
|
|
|
// Discover which activities are said to be running on threads currently |
|
|
const nub_size_t num_threads = thread_list.NumThreads(); |
|
|
for (nub_size_t i = 0; i < num_threads; ++i) |
|
|
{ |
|
|
nub_thread_t thread_id = thread_list.ThreadIDAtIndex(i); |
|
|
os_activity_t act = 0; |
|
|
if (m_os_activity_for_task_thread != nullptr) |
|
|
{ |
|
|
act = m_os_activity_for_task_thread (task, thread_id); |
|
|
} |
|
|
else if (m_os_activity_for_thread != nullptr) |
|
|
{ |
|
|
act = m_os_activity_for_thread (process_info, thread_id); |
|
|
} |
|
|
if (act != 0) |
|
|
thread_activity_mapping[thread_id] = act; |
|
|
} |
|
|
|
|
|
// Collect all Messages -- messages associated with a thread |
|
|
|
|
|
// When there's no genealogy information, an early version of os_activity_messages_for_thread |
|
|
// can crash in rare circumstances. Check to see if this process has any activities before |
|
|
// making the call to get messages. |
|
|
if (process_info->activities != nullptr && thread_activity_mapping.size() > 0) |
|
|
{ |
|
|
std::map<nub_thread_t, uint64_t>::const_iterator iter; |
|
|
for (iter = thread_activity_mapping.begin(); iter != thread_activity_mapping.end(); ++iter) |
|
|
{ |
|
|
nub_thread_t thread_id = iter->first; |
|
|
os_activity_t act = iter->second; |
|
|
os_trace_message_list_t this_thread_messages = m_os_activity_messages_for_thread (process_info, act, thread_id); |
|
|
m_os_activity_iterate_messages (this_thread_messages, process_info, ^bool(os_trace_message_t trace_msg) |
|
|
{ |
|
|
Message msg; |
|
|
msg.timestamp = trace_msg->timestamp; |
|
|
msg.trace_id = trace_msg->trace_id; |
|
|
msg.thread = trace_msg->thread; |
|
|
msg.type = m_os_trace_get_type (trace_msg); |
|
|
msg.activity_id = act; |
|
|
if (trace_msg->image_uuid && trace_msg->image_path) |
|
|
{ |
|
|
ProcessExecutableInfoSP process_info_sp (new ProcessExecutableInfo()); |
|
|
uuid_copy (process_info_sp->image_uuid, trace_msg->image_uuid); |
|
|
process_info_sp->image_path = trace_msg->image_path; |
|
|
msg.process_info_index = AddProcessExecutableInfo (process_info_sp); |
|
|
} |
|
|
const char *message_text = m_os_trace_copy_formatted_message (trace_msg); |
|
|
if (message_text) |
|
|
msg.message = message_text; |
|
|
messages.push_back (msg); |
|
|
return true; |
|
|
}); |
|
|
} |
|
|
} |
|
|
} |
|
|
return true; |
|
|
}); |
|
|
} |
|
|
dispatch_semaphore_signal(semaphore); |
|
|
}) == true) |
|
|
{ |
|
|
// Wait for the diagnosticd xpc calls to all finish up -- or half a second to elapse. |
|
|
dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC / 2); |
|
|
bool success = dispatch_semaphore_wait(semaphore, timeout) == 0; |
|
|
if (!success) |
|
|
{ |
|
|
m_diagnosticd_call_timed_out = true; |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
// breadcrumbs, activities, and messages have all now been filled in. |
|
|
|
|
|
std::map<nub_thread_t, uint64_t>::const_iterator iter; |
|
|
for (iter = thread_activity_mapping.begin(); iter != thread_activity_mapping.end(); ++iter) |
|
|
{ |
|
|
nub_thread_t thread_id = iter->first; |
|
|
uint64_t activity_id = iter->second; |
|
|
ActivityList::const_iterator activity_search; |
|
|
bool found_activity_for_this_thread = false; |
|
|
for (activity_search = activities.begin(); activity_search != activities.end(); ++activity_search) |
|
|
{ |
|
|
if (activity_search->activity_id == activity_id) |
|
|
{ |
|
|
found_activity_for_this_thread = true; |
|
|
ThreadActivitySP thread_activity_sp (new ThreadActivity()); |
|
|
thread_activity_sp->current_activity = *activity_search; |
|
|
|
|
|
BreadcrumbList::const_iterator breadcrumb_search; |
|
|
for (breadcrumb_search = breadcrumbs.begin(); breadcrumb_search != breadcrumbs.end(); ++breadcrumb_search) |
|
|
{ |
|
|
if (breadcrumb_search->activity_id == activity_id) |
|
|
{ |
|
|
thread_activity_sp->breadcrumbs.push_back (*breadcrumb_search); |
|
|
} |
|
|
} |
|
|
MessageList::const_iterator message_search; |
|
|
for (message_search = messages.begin(); message_search != messages.end(); ++message_search) |
|
|
{ |
|
|
if (message_search->thread == thread_id) |
|
|
{ |
|
|
thread_activity_sp->messages.push_back (*message_search); |
|
|
} |
|
|
} |
|
|
|
|
|
m_thread_activities[thread_id] = thread_activity_sp; |
|
|
break; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
uint32_t |
|
|
Genealogy::AddProcessExecutableInfo (ProcessExecutableInfoSP process_exe_info) |
|
|
{ |
|
|
const uint32_t info_size = m_process_executable_infos.size(); |
|
|
for (uint32_t idx = 0; idx < info_size; ++idx) |
|
|
{ |
|
|
if (uuid_compare (m_process_executable_infos[idx]->image_uuid, process_exe_info->image_uuid) == 0) |
|
|
{ |
|
|
return idx + 1; |
|
|
} |
|
|
} |
|
|
m_process_executable_infos.push_back (process_exe_info); |
|
|
return info_size + 1; |
|
|
} |
|
|
|
|
|
Genealogy::ProcessExecutableInfoSP |
|
|
Genealogy::GetProcessExecutableInfosAtIndex(uint32_t idx) |
|
|
{ |
|
|
ProcessExecutableInfoSP info_sp; |
|
|
if (idx > 0) |
|
|
{ |
|
|
idx--; |
|
|
if (idx <= m_process_executable_infos.size()) |
|
|
{ |
|
|
info_sp = m_process_executable_infos[idx]; |
|
|
} |
|
|
} |
|
|
return info_sp; |
|
|
} |
|
|
|