[Kernel] ExInitializeReadWriteLock
This commit is contained in:
parent
f3134e265a
commit
dcca5f587e
|
@ -0,0 +1,150 @@
|
||||||
|
/**
|
||||||
|
******************************************************************************
|
||||||
|
* 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
|
|
@ -0,0 +1,112 @@
|
||||||
|
/**
|
||||||
|
******************************************************************************
|
||||||
|
* 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_
|
|
@ -0,0 +1,29 @@
|
||||||
|
/**
|
||||||
|
******************************************************************************
|
||||||
|
* 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_
|
|
@ -1251,6 +1251,28 @@ SHIM_CALL KeRemoveQueueDpc_shim(PPCContext* ppc_context,
|
||||||
SHIM_SET_RETURN_32(result ? 1 : 0);
|
SHIM_SET_RETURN_32(result ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://github.com/Cxbx-Reloaded/Cxbx-Reloaded/blob/51e4dfcaacfdbd1a9692272931a436371492f72d/import/OpenXDK/include/xboxkrnl/xboxkrnl.h#L1372
|
||||||
|
struct X_ERWLOCK {
|
||||||
|
be<int32_t> lock_count; // 0x0
|
||||||
|
be<uint32_t> writers_waiting_count; // 0x4
|
||||||
|
be<uint32_t> readers_waiting_count; // 0x8
|
||||||
|
be<uint32_t> readers_entry_count; // 0xC
|
||||||
|
X_KEVENT writer_event; // 0x10
|
||||||
|
X_KSEMAPHORE reader_semaphore; // 0x20
|
||||||
|
be<uint32_t> spin_lock; // 0x34
|
||||||
|
};
|
||||||
|
|
||||||
|
void ExInitializeReadWriteLock(pointer_t<X_ERWLOCK> lock_ptr) {
|
||||||
|
lock_ptr->lock_count = -1;
|
||||||
|
lock_ptr->writers_waiting_count = 0;
|
||||||
|
lock_ptr->readers_waiting_count = 0;
|
||||||
|
lock_ptr->readers_entry_count = 0;
|
||||||
|
KeInitializeEvent(&lock_ptr->writer_event, 1, 0);
|
||||||
|
KeInitializeSemaphore(&lock_ptr->reader_semaphore, 0, 0x7FFFFFFF);
|
||||||
|
}
|
||||||
|
DECLARE_XBOXKRNL_EXPORT(ExInitializeReadWriteLock,
|
||||||
|
ExportTag::kThreading | ExportTag::kImplemented);
|
||||||
|
|
||||||
// NOTE: This function is very commonly inlined, and probably won't be called!
|
// NOTE: This function is very commonly inlined, and probably won't be called!
|
||||||
pointer_result_t InterlockedPushEntrySList(
|
pointer_result_t InterlockedPushEntrySList(
|
||||||
pointer_t<X_SLIST_HEADER> plist_ptr, pointer_t<X_SINGLE_LIST_ENTRY> entry) {
|
pointer_t<X_SLIST_HEADER> plist_ptr, pointer_t<X_SINGLE_LIST_ENTRY> entry) {
|
||||||
|
|
Loading…
Reference in New Issue