Remove some files that weren't even staged :|
This commit is contained in:
parent
212688249c
commit
e0cef9dcd6
|
@ -1,150 +0,0 @@
|
||||||
/**
|
|
||||||
******************************************************************************
|
|
||||||
* Xenia : Xbox 360 Emulator Research Project *
|
|
||||||
******************************************************************************
|
|
||||||
* Copyright 2015 Ben Vanik. All rights reserved. *
|
|
||||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
|
||||||
******************************************************************************
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "xenia/debug/profiler.h"
|
|
||||||
|
|
||||||
#include "xenia/base/mutex.h"
|
|
||||||
#include "xenia/cpu/backend/code_cache.h"
|
|
||||||
#include "xenia/cpu/ppc/ppc_opcode_info.h"
|
|
||||||
#include "xenia/cpu/stack_walker.h"
|
|
||||||
#include "xenia/debug/debugger.h"
|
|
||||||
#include "xenia/kernel/xthread.h"
|
|
||||||
#include "xenia/emulator.h"
|
|
||||||
|
|
||||||
// Credits go to Very Sleepy for the general design
|
|
||||||
// https://github.com/VerySleepy/verysleepy
|
|
||||||
|
|
||||||
namespace xe {
|
|
||||||
namespace debug {
|
|
||||||
|
|
||||||
Profiler::Profiler(Debugger* debugger) : debugger_(debugger) {}
|
|
||||||
|
|
||||||
void Profiler::Start() {
|
|
||||||
assert_false(is_profiling_);
|
|
||||||
|
|
||||||
total_samples_collected_.exchange(0);
|
|
||||||
is_profiling_ = true;
|
|
||||||
timer_.Start();
|
|
||||||
|
|
||||||
auto params = threading::Thread::CreationParameters();
|
|
||||||
worker_thread_ = xe::threading::Thread::Create(
|
|
||||||
params, std::bind(&Profiler::ProfilerEntry, this));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Profiler::Stop() {
|
|
||||||
assert_true(is_profiling_);
|
|
||||||
|
|
||||||
is_profiling_ = false;
|
|
||||||
timer_.Stop();
|
|
||||||
profiling_stop_fence_.Wait();
|
|
||||||
worker_thread_.reset();
|
|
||||||
|
|
||||||
CalculateSamplesPerInstruction();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Profiler::OnThreadsChanged() {
|
|
||||||
auto lock = global_critical_region::AcquireDirect();
|
|
||||||
should_refresh_threads_ = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Profiler::CalculateSamplesPerInstruction() {
|
|
||||||
auto cache = debugger_->emulator()->processor()->backend()->code_cache();
|
|
||||||
auto memory = debugger_->emulator()->memory();
|
|
||||||
|
|
||||||
for (auto& sample : samples_) {
|
|
||||||
// Translate x86 address to ppc address
|
|
||||||
auto host_addr = sample.first.second.host_frames[0];
|
|
||||||
auto func = cache->LookupFunction(host_addr);
|
|
||||||
if (!func) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t ppc_addr = func->MapMachineCodeToGuestAddress(host_addr);
|
|
||||||
auto code = xe::load_and_swap<uint32_t>(memory->TranslateVirtual(ppc_addr));
|
|
||||||
auto& disasm = cpu::ppc::GetOpcodeDisasmInfo(cpu::ppc::LookupOpcode(code));
|
|
||||||
samples_per_instruction_[disasm.name] += sample.second;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Profiler::CalculateSamplesPerFunction() {
|
|
||||||
auto cache = debugger_->emulator()->processor()->backend()->code_cache();
|
|
||||||
auto memory = debugger_->emulator()->memory();
|
|
||||||
|
|
||||||
for (auto& sample : samples_) {
|
|
||||||
// Translate x86 address to ppc address
|
|
||||||
auto host_addr = sample.first.second.host_frames[0];
|
|
||||||
auto func = cache->LookupFunction(host_addr);
|
|
||||||
if (!func) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
samples_per_function_[func] += sample.second;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Profiler::ProfilerEntry() {
|
|
||||||
threading::Thread::GetCurrentThread()->set_name("Profiler Thread");
|
|
||||||
std::vector<threading::Thread*> thread_list;
|
|
||||||
|
|
||||||
// Fetch a list of threads from the debugger.
|
|
||||||
should_refresh_threads_ = true;
|
|
||||||
|
|
||||||
// Called when we should profile.
|
|
||||||
while (is_profiling_) {
|
|
||||||
if (should_refresh_threads_) {
|
|
||||||
auto lock = global_critical_region::AcquireDirect();
|
|
||||||
auto exec_infos = debugger_->QueryThreadExecutionInfos();
|
|
||||||
for (auto info : exec_infos) {
|
|
||||||
if (info->thread && !info->thread->is_host_object()) {
|
|
||||||
thread_list.push_back(info->thread->thread());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
should_refresh_threads_ = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reshuffle the thread list so we don't starve threads. Probably don't need
|
|
||||||
// to do this every run.
|
|
||||||
std::random_shuffle(thread_list.begin(), thread_list.end());
|
|
||||||
for (size_t i = 0; i < thread_list.size(); i++) {
|
|
||||||
// FIXME: This is totally a race condition. We can't take the global lock
|
|
||||||
// though, because that would screw with profiling.
|
|
||||||
if (should_refresh_threads_) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
SampleThread(thread_list[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
profiling_stop_fence_.Signal();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Profiler::SampleThread(threading::Thread* thread) {
|
|
||||||
auto stack_walker = debugger_->emulator()->processor()->stack_walker();
|
|
||||||
thread->Suspend();
|
|
||||||
|
|
||||||
CallStack stack;
|
|
||||||
stack.depth = stack_walker->CaptureStackTrace(
|
|
||||||
thread->native_handle(), stack.host_frames, 0,
|
|
||||||
xe::countof(stack.host_frames), nullptr, nullptr);
|
|
||||||
assert_true(stack.depth > 0);
|
|
||||||
|
|
||||||
thread->Resume();
|
|
||||||
|
|
||||||
// Discard the sample if a stack trace could not be captured.
|
|
||||||
if (stack.depth > 0) {
|
|
||||||
samples_[{thread->system_id(), stack}]++;
|
|
||||||
total_samples_[thread->system_id()]++;
|
|
||||||
total_samples_collected_.fetch_add(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace debug
|
|
||||||
} // namespace xe
|
|
|
@ -1,112 +0,0 @@
|
||||||
/**
|
|
||||||
******************************************************************************
|
|
||||||
* Xenia : Xbox 360 Emulator Research Project *
|
|
||||||
******************************************************************************
|
|
||||||
* Copyright 2015 Ben Vanik. All rights reserved. *
|
|
||||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
|
||||||
******************************************************************************
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef XENIA_DEBUG_PROFILER_H_
|
|
||||||
#define XENIA_DEBUG_PROFILER_H_
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <map>
|
|
||||||
|
|
||||||
#include "xenia/base/assert.h"
|
|
||||||
#include "xenia/base/clock.h"
|
|
||||||
#include "xenia/base/threading.h"
|
|
||||||
|
|
||||||
namespace xe {
|
|
||||||
namespace kernel {
|
|
||||||
class XThread;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace cpu {
|
|
||||||
class Function;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace debug {
|
|
||||||
class Debugger;
|
|
||||||
|
|
||||||
class Profiler {
|
|
||||||
public:
|
|
||||||
Profiler(Debugger* debugger);
|
|
||||||
|
|
||||||
// Time elapsed between profiling start and stop (or now)
|
|
||||||
uint64_t GetTicksElapsed() { return timer_.GetTicksElapsed(); }
|
|
||||||
double GetSecondsElapsed() { return timer_.GetSecondsElapsed(); }
|
|
||||||
|
|
||||||
void Start();
|
|
||||||
void Stop();
|
|
||||||
|
|
||||||
// Internal - do not use.
|
|
||||||
void OnThreadsChanged();
|
|
||||||
|
|
||||||
struct CallStack {
|
|
||||||
uint64_t host_frames[128];
|
|
||||||
size_t depth;
|
|
||||||
|
|
||||||
bool operator<(const CallStack& other) const {
|
|
||||||
if (depth != other.depth) {
|
|
||||||
return depth < other.depth;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 0; i < depth; i++) {
|
|
||||||
if (host_frames[i] != other.host_frames[i]) {
|
|
||||||
return host_frames[i] < other.host_frames[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
bool is_profiling() const { return is_profiling_; }
|
|
||||||
std::map<std::pair<uint64_t, CallStack>, uint64_t>& GetSamplesPerThread() {
|
|
||||||
assert_false(is_profiling_);
|
|
||||||
return samples_;
|
|
||||||
}
|
|
||||||
std::map<uint64_t, uint64_t>& GetTotalSamplesPerThread() {
|
|
||||||
assert_false(is_profiling_);
|
|
||||||
return total_samples_;
|
|
||||||
}
|
|
||||||
std::map<const char*, uint64_t>& GetSamplesPerInstruction() {
|
|
||||||
assert_false(is_profiling_);
|
|
||||||
return samples_per_instruction_;
|
|
||||||
}
|
|
||||||
uint64_t total_samples_collected() { return total_samples_collected_.load(); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
void CalculateSamplesPerInstruction();
|
|
||||||
void CalculateSamplesPerFunction();
|
|
||||||
|
|
||||||
void ProfilerEntry();
|
|
||||||
|
|
||||||
void SampleThread(threading::Thread* thread);
|
|
||||||
|
|
||||||
Debugger* debugger_ = nullptr;
|
|
||||||
|
|
||||||
// TODO: Callstack map
|
|
||||||
// Map of {thread_id, stack} => # hits
|
|
||||||
std::map<std::pair<uint64_t, CallStack>, uint64_t> samples_;
|
|
||||||
std::map<uint64_t, uint64_t> total_samples_; // thread_id => total samples
|
|
||||||
std::atomic<uint64_t> total_samples_collected_;
|
|
||||||
|
|
||||||
std::map<const char*, uint64_t> samples_per_instruction_;
|
|
||||||
std::map<cpu::Function*, uint64_t> samples_per_function_;
|
|
||||||
|
|
||||||
bool is_profiling_ = false;
|
|
||||||
bool worker_should_exit_ = false;
|
|
||||||
bool should_refresh_threads_ = false;
|
|
||||||
threading::Fence worker_fence_;
|
|
||||||
threading::Fence profiling_stop_fence_;
|
|
||||||
std::unique_ptr<threading::Thread> worker_thread_;
|
|
||||||
|
|
||||||
Timer timer_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace debug
|
|
||||||
} // namespace xe
|
|
||||||
|
|
||||||
#endif // XENIA_DEBUG_PROFILER_H_
|
|
|
@ -1,29 +0,0 @@
|
||||||
/**
|
|
||||||
******************************************************************************
|
|
||||||
* Xenia : Xbox 360 Emulator Research Project *
|
|
||||||
******************************************************************************
|
|
||||||
* Copyright 2015 Ben Vanik. All rights reserved. *
|
|
||||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
|
||||||
******************************************************************************
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef XENIA_DEBUG_PROTOCOL_H_
|
|
||||||
#define XENIA_DEBUG_PROTOCOL_H_
|
|
||||||
|
|
||||||
namespace xe {
|
|
||||||
namespace debug {
|
|
||||||
namespace protocol {
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
class Packet {
|
|
||||||
public:
|
|
||||||
|
|
||||||
protected:
|
|
||||||
int type_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace protocol
|
|
||||||
} // namespace debug
|
|
||||||
} // namespace xe
|
|
||||||
|
|
||||||
#endif // XENIA_DEBUG_PROTOCOL_H_
|
|
Loading…
Reference in New Issue