320 lines
14 KiB
C++
320 lines
14 KiB
C++
///===-- 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 <dlfcn.h>
|
|
#include <string>
|
|
#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;
|
|
for (activity_search = activities.begin();
|
|
activity_search != activities.end(); ++activity_search) {
|
|
if (activity_search->activity_id == activity_id) {
|
|
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 =
|
|
static_cast<uint32_t>(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(size_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;
|
|
}
|