-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
56017f6
commit 8df984f
Showing
4 changed files
with
172 additions
and
137 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,4 +8,5 @@ ETTrace-iphoneos.xcarchive/ | |
output.json | ||
output.folded | ||
.swiftpm | ||
.build | ||
.build | ||
output_*.json |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
#include "EMGStackTraceRecorder.h" | ||
|
||
#import <QuartzCore/QuartzCore.h> | ||
#import <mach-o/arch.h> | ||
#import <mach/mach.h> | ||
#import <pthread.h> | ||
#import <deque> | ||
#import <iostream> | ||
#import <mutex> | ||
#import <unordered_map> | ||
|
||
extern "C" { | ||
void FIRCLSWriteThreadStack(thread_t thread, uintptr_t *frames, uint64_t framesCapacity, uint64_t *framesWritten); | ||
} | ||
|
||
static const int kMaxFramesPerStack = 1024; | ||
|
||
kern_return_t checkMachCall(kern_return_t result) { | ||
if (result != KERN_SUCCESS) { | ||
std::cerr << "Mach call failed with " << result << std::endl; | ||
} | ||
return result; | ||
} | ||
|
||
Thread::Thread(thread_t threadId, thread_t mainThreadId) { | ||
name = "Failed to get name"; // Error case | ||
|
||
if(threadId == mainThreadId) { | ||
name = "Main Thread"; | ||
} else { | ||
// Get thread Name | ||
char cName[1024]; | ||
pthread_t pt = pthread_from_mach_thread_np(threadId); | ||
if (pt) { | ||
int rc = pthread_getname_np(pt, cName, sizeof(cName)); | ||
if (rc == 0) { | ||
name = cName; | ||
} | ||
} | ||
} | ||
} | ||
|
||
std::vector<ThreadSummary> EMGStackTraceRecorder::collectThreadSummaries() { | ||
std::lock_guard<std::mutex> lockGuard(threadsLock); | ||
|
||
std::vector<ThreadSummary> summaries; | ||
for (const auto &[threadId, thread] : threadsMap) { | ||
std::vector<StackSummary> stackSummaries; | ||
for (const auto &stack : thread.stacks) { | ||
std::vector<uintptr_t> addresses; | ||
for (auto i = stack.storageStartIndex; i < stack.storageEndIndex; i++) { | ||
addresses.emplace_back(addressStorage[i]); | ||
} | ||
// Reverse the stack addresses to get the correct order | ||
std::reverse(addresses.begin(), addresses.end()); | ||
stackSummaries.emplace_back(stack.time, addresses); | ||
} | ||
summaries.emplace_back(threadId, thread.name, stackSummaries); | ||
} | ||
return summaries; | ||
} | ||
|
||
void EMGStackTraceRecorder::recordStackForAllThreads(bool recordAllThreads, thread_t mainMachThread, thread_t etTraceThread) { | ||
std::lock_guard<std::mutex> lockGuard(threadsLock); | ||
thread_act_array_t threads = nullptr; | ||
mach_msg_type_number_t threadCount = 0; | ||
if (recordAllThreads) { | ||
int result = checkMachCall(task_threads(mach_task_self(), &threads, &threadCount)); | ||
if (result != KERN_SUCCESS) { | ||
threadCount = 0; | ||
} | ||
} else { | ||
threads = &mainMachThread; | ||
threadCount = 1; | ||
} | ||
|
||
// This time gets less accurate for later threads, but still good | ||
CFTimeInterval time = CACurrentMediaTime(); | ||
for (mach_msg_type_number_t i = 0; i < threadCount; i++) { | ||
if (threads[i] == etTraceThread) { | ||
continue; | ||
} | ||
|
||
uintptr_t frames[kMaxFramesPerStack]; | ||
uint64_t frameCount = 0; | ||
|
||
if (thread_suspend(threads[i]) != KERN_SUCCESS) { | ||
// In theory, the thread may have been destroyed by now, so we exit early if this fails | ||
continue; | ||
} | ||
// BEGIN REENTRANT SECTION | ||
FIRCLSWriteThreadStack(threads[i], frames, kMaxFramesPerStack, &frameCount); | ||
// END REENTRANT SECTION | ||
checkMachCall(thread_resume(threads[i])); | ||
|
||
auto emplaceResult = threadsMap.try_emplace(threads[i], threads[i], mainMachThread); | ||
size_t startIndex = addressStorage.size(); | ||
for (int frame_idx = 0; frame_idx < frameCount; frame_idx++) { | ||
addressStorage.emplace_back(frames[frame_idx]); | ||
} | ||
size_t endIndex = addressStorage.size(); | ||
emplaceResult.first->second.stacks.emplace_back(time, startIndex, endIndex); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
#import <deque> | ||
#import <vector> | ||
#import <unordered_map> | ||
#import <mach/mach.h> | ||
#import <QuartzCore/QuartzCore.h> | ||
#import <iostream> | ||
|
||
struct StackSummary { | ||
CFTimeInterval time; | ||
std::vector<uintptr_t> stack; | ||
|
||
StackSummary(CFTimeInterval time, std::vector<uintptr_t> &stack) : time(time), stack(stack) { | ||
} | ||
}; | ||
|
||
struct ThreadSummary { | ||
thread_t threadId; | ||
std::string name; | ||
std::vector<StackSummary> stacks; | ||
|
||
ThreadSummary(thread_t threadId, const std::string &name, std::vector<StackSummary> &stacks) : threadId(threadId), name(name), stacks(stacks) { | ||
} | ||
}; | ||
|
||
struct Stack { | ||
CFTimeInterval time; | ||
size_t storageStartIndex; // Inclusive | ||
size_t storageEndIndex; // Exclusive | ||
|
||
Stack(CFTimeInterval time, size_t storageStartIndex, size_t storageEndIndex) : time(time), storageStartIndex(storageStartIndex), storageEndIndex(storageEndIndex) { | ||
} | ||
}; | ||
|
||
struct Thread { | ||
std::deque<Stack> stacks; | ||
std::string name; | ||
|
||
Thread(thread_t threadId, thread_t mainThreadId); | ||
}; | ||
|
||
class EMGStackTraceRecorder { | ||
std::unordered_map<unsigned int, Thread> threadsMap; | ||
std::mutex threadsLock; | ||
std::deque<uintptr_t> addressStorage; | ||
|
||
public: | ||
void recordStackForAllThreads(bool recordAllThreads, thread_t mainMachThread, thread_t etTraceThread); | ||
|
||
std::vector<ThreadSummary> collectThreadSummaries(); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters