[Kernel] ExInitializeReadWriteLock

This commit is contained in:
Dr. Chat 2018-05-07 18:46:15 -05:00
parent f3134e265a
commit dcca5f587e
4 changed files with 313 additions and 0 deletions

150
src/xenia/debug/profiler.cc Normal file
View File

@ -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

112
src/xenia/debug/profiler.h Normal file
View File

@ -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_

View File

@ -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_

View File

@ -1251,6 +1251,28 @@ SHIM_CALL KeRemoveQueueDpc_shim(PPCContext* ppc_context,
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!
pointer_result_t InterlockedPushEntrySList(
pointer_t<X_SLIST_HEADER> plist_ptr, pointer_t<X_SINGLE_LIST_ENTRY> entry) {