This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
file(GLOB_RECURSE FILE_SOURCES "*.h" "*.c" "*.cpp")
|
file(GLOB_RECURSE FILE_SOURCES CONFIGURE_DEPENDS "*.h" "*.c" "*.cpp")
|
||||||
|
|
||||||
add_library(PackLib STATIC ${FILE_SOURCES})
|
add_library(PackLib STATIC ${FILE_SOURCES})
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
#include "M2Pack.h"
|
#include "M2Pack.h"
|
||||||
|
#include "PackProfile.h"
|
||||||
#include "M2PackRuntimeKeyProvider.h"
|
#include "M2PackRuntimeKeyProvider.h"
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
#include <zstd.h>
|
#include <zstd.h>
|
||||||
@@ -40,6 +42,7 @@ bool ReadPod(const uint8_t* bytes, std::size_t size, std::size_t& offset, T& out
|
|||||||
|
|
||||||
bool CM2Pack::Load(const std::string& path)
|
bool CM2Pack::Load(const std::string& path)
|
||||||
{
|
{
|
||||||
|
m_source_path = path;
|
||||||
std::error_code ec;
|
std::error_code ec;
|
||||||
m_file.map(path, ec);
|
m_file.map(path, ec);
|
||||||
|
|
||||||
@@ -100,6 +103,7 @@ bool CM2Pack::Load(const std::string& path)
|
|||||||
bool CM2Pack::ValidateManifest()
|
bool CM2Pack::ValidateManifest()
|
||||||
{
|
{
|
||||||
std::array<uint8_t, M2PACK_HASH_SIZE> manifest_hash {};
|
std::array<uint8_t, M2PACK_HASH_SIZE> manifest_hash {};
|
||||||
|
const auto hashStart = std::chrono::steady_clock::now();
|
||||||
crypto_generichash(
|
crypto_generichash(
|
||||||
manifest_hash.data(),
|
manifest_hash.data(),
|
||||||
manifest_hash.size(),
|
manifest_hash.size(),
|
||||||
@@ -107,6 +111,13 @@ bool CM2Pack::ValidateManifest()
|
|||||||
m_manifest_bytes.size(),
|
m_manifest_bytes.size(),
|
||||||
nullptr,
|
nullptr,
|
||||||
0);
|
0);
|
||||||
|
RecordPackProfileStage(
|
||||||
|
"m2p",
|
||||||
|
"manifest_hash",
|
||||||
|
m_manifest_bytes.size(),
|
||||||
|
manifest_hash.size(),
|
||||||
|
static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::microseconds>(
|
||||||
|
std::chrono::steady_clock::now() - hashStart).count()));
|
||||||
|
|
||||||
if (memcmp(manifest_hash.data(), m_header.manifest_hash, manifest_hash.size()) != 0)
|
if (memcmp(manifest_hash.data(), m_header.manifest_hash, manifest_hash.size()) != 0)
|
||||||
{
|
{
|
||||||
@@ -121,17 +132,33 @@ bool CM2Pack::ValidateManifest()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto verifyStart = std::chrono::steady_clock::now();
|
||||||
if (crypto_sign_verify_detached(
|
if (crypto_sign_verify_detached(
|
||||||
m_header.manifest_signature,
|
m_header.manifest_signature,
|
||||||
m_manifest_bytes.data(),
|
m_manifest_bytes.data(),
|
||||||
m_manifest_bytes.size(),
|
m_manifest_bytes.size(),
|
||||||
publicKey->data()) != 0)
|
publicKey->data()) != 0)
|
||||||
{
|
{
|
||||||
|
RecordPackProfileStage(
|
||||||
|
"m2p",
|
||||||
|
"manifest_signature",
|
||||||
|
m_manifest_bytes.size(),
|
||||||
|
M2PACK_SIGNATURE_SIZE,
|
||||||
|
static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::microseconds>(
|
||||||
|
std::chrono::steady_clock::now() - verifyStart).count()));
|
||||||
TraceError("CM2Pack::ValidateManifest: manifest signature mismatch");
|
TraceError("CM2Pack::ValidateManifest: manifest signature mismatch");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
RecordPackProfileStage(
|
||||||
|
"m2p",
|
||||||
|
"manifest_signature",
|
||||||
|
m_manifest_bytes.size(),
|
||||||
|
M2PACK_SIGNATURE_SIZE,
|
||||||
|
static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::microseconds>(
|
||||||
|
std::chrono::steady_clock::now() - verifyStart).count()));
|
||||||
|
|
||||||
std::size_t offset = 0;
|
std::size_t offset = 0;
|
||||||
|
const auto parseStart = std::chrono::steady_clock::now();
|
||||||
TM2PackManifestHeader manifest_header {};
|
TM2PackManifestHeader manifest_header {};
|
||||||
if (!ReadPod(m_manifest_bytes.data(), m_manifest_bytes.size(), offset, manifest_header))
|
if (!ReadPod(m_manifest_bytes.data(), m_manifest_bytes.size(), offset, manifest_header))
|
||||||
{
|
{
|
||||||
@@ -184,6 +211,13 @@ bool CM2Pack::ValidateManifest()
|
|||||||
m_index.push_back(std::move(entry));
|
m_index.push_back(std::move(entry));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RecordPackProfileStage(
|
||||||
|
"m2p",
|
||||||
|
"manifest_parse",
|
||||||
|
m_manifest_bytes.size(),
|
||||||
|
m_index.size(),
|
||||||
|
static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::microseconds>(
|
||||||
|
std::chrono::steady_clock::now() - parseStart).count()));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,6 +230,7 @@ bool CM2Pack::DecryptEntryPayload(const TM2PackEntry& entry, std::vector<uint8_t
|
|||||||
decrypted = pPool->Acquire(entry.stored_size);
|
decrypted = pPool->Acquire(entry.stored_size);
|
||||||
}
|
}
|
||||||
decrypted.resize(entry.stored_size);
|
decrypted.resize(entry.stored_size);
|
||||||
|
const auto decryptStart = std::chrono::steady_clock::now();
|
||||||
unsigned long long written = 0;
|
unsigned long long written = 0;
|
||||||
if (crypto_aead_xchacha20poly1305_ietf_decrypt(
|
if (crypto_aead_xchacha20poly1305_ietf_decrypt(
|
||||||
decrypted.data(),
|
decrypted.data(),
|
||||||
@@ -214,6 +249,13 @@ bool CM2Pack::DecryptEntryPayload(const TM2PackEntry& entry, std::vector<uint8_t
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
RecordPackProfileStage(
|
||||||
|
"m2p",
|
||||||
|
"aead_decrypt",
|
||||||
|
entry.stored_size,
|
||||||
|
written,
|
||||||
|
static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::microseconds>(
|
||||||
|
std::chrono::steady_clock::now() - decryptStart).count()));
|
||||||
|
|
||||||
decrypted.resize(static_cast<std::size_t>(written));
|
decrypted.resize(static_cast<std::size_t>(written));
|
||||||
return true;
|
return true;
|
||||||
@@ -243,7 +285,15 @@ bool CM2Pack::GetFileWithPool(const TM2PackEntry& entry, std::vector<uint8_t>& r
|
|||||||
{
|
{
|
||||||
result.resize(entry.original_size);
|
result.resize(entry.original_size);
|
||||||
ZSTD_DCtx* dctx = GetThreadLocalZstdContext();
|
ZSTD_DCtx* dctx = GetThreadLocalZstdContext();
|
||||||
|
const auto decompressStart = std::chrono::steady_clock::now();
|
||||||
size_t written = ZSTD_decompressDCtx(dctx, result.data(), result.size(), compressed.data(), compressed.size());
|
size_t written = ZSTD_decompressDCtx(dctx, result.data(), result.size(), compressed.data(), compressed.size());
|
||||||
|
RecordPackProfileStage(
|
||||||
|
"m2p",
|
||||||
|
"zstd_decompress",
|
||||||
|
compressed.size(),
|
||||||
|
ZSTD_isError(written) ? 0 : written,
|
||||||
|
static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::microseconds>(
|
||||||
|
std::chrono::steady_clock::now() - decompressStart).count()));
|
||||||
if (pPool)
|
if (pPool)
|
||||||
{
|
{
|
||||||
pPool->Release(std::move(compressed));
|
pPool->Release(std::move(compressed));
|
||||||
@@ -271,6 +321,7 @@ bool CM2Pack::GetFileWithPool(const TM2PackEntry& entry, std::vector<uint8_t>& r
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::array<uint8_t, M2PACK_HASH_SIZE> plain_hash {};
|
std::array<uint8_t, M2PACK_HASH_SIZE> plain_hash {};
|
||||||
|
const auto hashStart = std::chrono::steady_clock::now();
|
||||||
crypto_generichash(
|
crypto_generichash(
|
||||||
plain_hash.data(),
|
plain_hash.data(),
|
||||||
plain_hash.size(),
|
plain_hash.size(),
|
||||||
@@ -278,6 +329,13 @@ bool CM2Pack::GetFileWithPool(const TM2PackEntry& entry, std::vector<uint8_t>& r
|
|||||||
result.size(),
|
result.size(),
|
||||||
nullptr,
|
nullptr,
|
||||||
0);
|
0);
|
||||||
|
RecordPackProfileStage(
|
||||||
|
"m2p",
|
||||||
|
"plaintext_hash",
|
||||||
|
result.size(),
|
||||||
|
plain_hash.size(),
|
||||||
|
static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::microseconds>(
|
||||||
|
std::chrono::steady_clock::now() - hashStart).count()));
|
||||||
|
|
||||||
if (memcmp(plain_hash.data(), entry.plaintext_hash.data(), plain_hash.size()) != 0)
|
if (memcmp(plain_hash.data(), entry.plaintext_hash.data(), plain_hash.size()) != 0)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ public:
|
|||||||
|
|
||||||
bool Load(const std::string& path);
|
bool Load(const std::string& path);
|
||||||
const std::vector<TM2PackEntry>& GetIndex() const { return m_index; }
|
const std::vector<TM2PackEntry>& GetIndex() const { return m_index; }
|
||||||
|
const std::string& GetSourcePath() const { return m_source_path; }
|
||||||
|
|
||||||
bool GetFile(const TM2PackEntry& entry, std::vector<uint8_t>& result);
|
bool GetFile(const TM2PackEntry& entry, std::vector<uint8_t>& result);
|
||||||
bool GetFileWithPool(const TM2PackEntry& entry, std::vector<uint8_t>& result, CBufferPool* pPool);
|
bool GetFileWithPool(const TM2PackEntry& entry, std::vector<uint8_t>& result, CBufferPool* pPool);
|
||||||
@@ -82,6 +83,7 @@ private:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
TM2PackHeader m_header {};
|
TM2PackHeader m_header {};
|
||||||
|
std::string m_source_path;
|
||||||
std::vector<uint8_t> m_manifest_bytes;
|
std::vector<uint8_t> m_manifest_bytes;
|
||||||
std::vector<TM2PackEntry> m_index;
|
std::vector<TM2PackEntry> m_index;
|
||||||
mio::mmap_source m_file;
|
mio::mmap_source m_file;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "M2PackRuntimeKeyProvider.h"
|
#include "M2PackRuntimeKeyProvider.h"
|
||||||
|
#include "PackProfile.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
@@ -238,6 +239,8 @@ void ApplyCommandLineOption(const std::string& key, const std::string& value)
|
|||||||
|
|
||||||
bool InitializeM2PackRuntimeKeyProvider(const char* commandLine)
|
bool InitializeM2PackRuntimeKeyProvider(const char* commandLine)
|
||||||
{
|
{
|
||||||
|
InitializePackProfile(commandLine);
|
||||||
|
|
||||||
if (g_state.initialized)
|
if (g_state.initialized)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
#include "Pack.h"
|
#include "Pack.h"
|
||||||
|
#include "PackProfile.h"
|
||||||
#include "EterLib/BufferPool.h"
|
#include "EterLib/BufferPool.h"
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
#include <zstd.h>
|
#include <zstd.h>
|
||||||
|
|
||||||
static thread_local ZSTD_DCtx* g_zstdDCtx = nullptr;
|
static thread_local ZSTD_DCtx* g_zstdDCtx = nullptr;
|
||||||
@@ -20,6 +23,7 @@ void CPack::DecryptData(uint8_t* data, size_t len, const uint8_t* nonce)
|
|||||||
|
|
||||||
bool CPack::Load(const std::string& path)
|
bool CPack::Load(const std::string& path)
|
||||||
{
|
{
|
||||||
|
m_source_path = path;
|
||||||
std::error_code ec;
|
std::error_code ec;
|
||||||
m_file.map(path, ec);
|
m_file.map(path, ec);
|
||||||
|
|
||||||
@@ -68,7 +72,15 @@ bool CPack::GetFileWithPool(const TPackFileEntry& entry, TPackFile& result, CBuf
|
|||||||
switch (entry.encryption)
|
switch (entry.encryption)
|
||||||
{
|
{
|
||||||
case 0: {
|
case 0: {
|
||||||
|
const auto decompressStart = std::chrono::steady_clock::now();
|
||||||
size_t decompressed_size = ZSTD_decompressDCtx(dctx, result.data(), result.size(), m_file.data() + offset, entry.compressed_size);
|
size_t decompressed_size = ZSTD_decompressDCtx(dctx, result.data(), result.size(), m_file.data() + offset, entry.compressed_size);
|
||||||
|
RecordPackProfileStage(
|
||||||
|
"pck",
|
||||||
|
"zstd_decompress",
|
||||||
|
entry.compressed_size,
|
||||||
|
ZSTD_isError(decompressed_size) ? 0 : decompressed_size,
|
||||||
|
static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::microseconds>(
|
||||||
|
std::chrono::steady_clock::now() - decompressStart).count()));
|
||||||
if (decompressed_size != entry.file_size) {
|
if (decompressed_size != entry.file_size) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -83,9 +95,25 @@ bool CPack::GetFileWithPool(const TPackFileEntry& entry, TPackFile& result, CBuf
|
|||||||
|
|
||||||
memcpy(compressed_data.data(), m_file.data() + offset, entry.compressed_size);
|
memcpy(compressed_data.data(), m_file.data() + offset, entry.compressed_size);
|
||||||
|
|
||||||
|
const auto decryptStart = std::chrono::steady_clock::now();
|
||||||
DecryptData(compressed_data.data(), entry.compressed_size, entry.nonce);
|
DecryptData(compressed_data.data(), entry.compressed_size, entry.nonce);
|
||||||
|
RecordPackProfileStage(
|
||||||
|
"pck",
|
||||||
|
"xchacha20_decrypt",
|
||||||
|
entry.compressed_size,
|
||||||
|
entry.compressed_size,
|
||||||
|
static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::microseconds>(
|
||||||
|
std::chrono::steady_clock::now() - decryptStart).count()));
|
||||||
|
|
||||||
|
const auto decompressStart = std::chrono::steady_clock::now();
|
||||||
size_t decompressed_size = ZSTD_decompressDCtx(dctx, result.data(), result.size(), compressed_data.data(), compressed_data.size());
|
size_t decompressed_size = ZSTD_decompressDCtx(dctx, result.data(), result.size(), compressed_data.data(), compressed_data.size());
|
||||||
|
RecordPackProfileStage(
|
||||||
|
"pck",
|
||||||
|
"zstd_decompress",
|
||||||
|
compressed_data.size(),
|
||||||
|
ZSTD_isError(decompressed_size) ? 0 : decompressed_size,
|
||||||
|
static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::microseconds>(
|
||||||
|
std::chrono::steady_clock::now() - decompressStart).count()));
|
||||||
|
|
||||||
if (pPool) {
|
if (pPool) {
|
||||||
pPool->Release(std::move(compressed_data));
|
pPool->Release(std::move(compressed_data));
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ public:
|
|||||||
|
|
||||||
bool Load(const std::string& path);
|
bool Load(const std::string& path);
|
||||||
const std::vector<TPackFileEntry>& GetIndex() const { return m_index; }
|
const std::vector<TPackFileEntry>& GetIndex() const { return m_index; }
|
||||||
|
const std::string& GetSourcePath() const { return m_source_path; }
|
||||||
|
|
||||||
bool GetFile(const TPackFileEntry& entry, TPackFile& result);
|
bool GetFile(const TPackFileEntry& entry, TPackFile& result);
|
||||||
bool GetFileWithPool(const TPackFileEntry& entry, TPackFile& result, CBufferPool* pPool);
|
bool GetFileWithPool(const TPackFileEntry& entry, TPackFile& result, CBufferPool* pPool);
|
||||||
@@ -22,6 +23,7 @@ private:
|
|||||||
void DecryptData(uint8_t* data, size_t len, const uint8_t* nonce);
|
void DecryptData(uint8_t* data, size_t len, const uint8_t* nonce);
|
||||||
|
|
||||||
TPackFileHeader m_header;
|
TPackFileHeader m_header;
|
||||||
|
std::string m_source_path;
|
||||||
std::vector<TPackFileEntry> m_index;
|
std::vector<TPackFileEntry> m_index;
|
||||||
mio::mmap_source m_file;
|
mio::mmap_source m_file;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
#include "PackManager.h"
|
#include "PackManager.h"
|
||||||
|
#include "PackProfile.h"
|
||||||
#include "EterLib/BufferPool.h"
|
#include "EterLib/BufferPool.h"
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@@ -27,8 +30,16 @@ bool CPackManager::AddPack(const std::string& path)
|
|||||||
if (packPath.extension() == ".m2p")
|
if (packPath.extension() == ".m2p")
|
||||||
{
|
{
|
||||||
std::shared_ptr<CM2Pack> pack = std::make_shared<CM2Pack>();
|
std::shared_ptr<CM2Pack> pack = std::make_shared<CM2Pack>();
|
||||||
|
const auto loadStart = std::chrono::steady_clock::now();
|
||||||
if (!pack->Load(path))
|
if (!pack->Load(path))
|
||||||
{
|
{
|
||||||
|
RecordPackProfileMount(
|
||||||
|
"m2p",
|
||||||
|
path,
|
||||||
|
false,
|
||||||
|
0,
|
||||||
|
static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::microseconds>(
|
||||||
|
std::chrono::steady_clock::now() - loadStart).count()));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,14 +49,29 @@ bool CPackManager::AddPack(const std::string& path)
|
|||||||
{
|
{
|
||||||
m_m2_entries[entry.path] = std::make_pair(pack, entry);
|
m_m2_entries[entry.path] = std::make_pair(pack, entry);
|
||||||
}
|
}
|
||||||
|
RecordPackProfileMount(
|
||||||
|
"m2p",
|
||||||
|
path,
|
||||||
|
true,
|
||||||
|
index.size(),
|
||||||
|
static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::microseconds>(
|
||||||
|
std::chrono::steady_clock::now() - loadStart).count()));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<CPack> pack = std::make_shared<CPack>();
|
std::shared_ptr<CPack> pack = std::make_shared<CPack>();
|
||||||
|
const auto loadStart = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
if (!pack->Load(path))
|
if (!pack->Load(path))
|
||||||
{
|
{
|
||||||
|
RecordPackProfileMount(
|
||||||
|
"pck",
|
||||||
|
path,
|
||||||
|
false,
|
||||||
|
0,
|
||||||
|
static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::microseconds>(
|
||||||
|
std::chrono::steady_clock::now() - loadStart).count()));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,6 +81,13 @@ bool CPackManager::AddPack(const std::string& path)
|
|||||||
{
|
{
|
||||||
m_entries[entry.file_name] = std::make_pair(pack, entry);
|
m_entries[entry.file_name] = std::make_pair(pack, entry);
|
||||||
}
|
}
|
||||||
|
RecordPackProfileMount(
|
||||||
|
"pck",
|
||||||
|
path,
|
||||||
|
true,
|
||||||
|
index.size(),
|
||||||
|
static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::microseconds>(
|
||||||
|
std::chrono::steady_clock::now() - loadStart).count()));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -73,16 +106,37 @@ bool CPackManager::GetFileWithPool(std::string_view path, TPackFile& result, CBu
|
|||||||
if (m_load_from_pack) {
|
if (m_load_from_pack) {
|
||||||
auto m2it = m_m2_entries.find(buf);
|
auto m2it = m_m2_entries.find(buf);
|
||||||
if (m2it != m_m2_entries.end()) {
|
if (m2it != m_m2_entries.end()) {
|
||||||
return m2it->second.first->GetFileWithPool(m2it->second.second, result, pPool);
|
const auto loadStart = std::chrono::steady_clock::now();
|
||||||
|
const bool ok = m2it->second.first->GetFileWithPool(m2it->second.second, result, pPool);
|
||||||
|
RecordPackProfileLoad(
|
||||||
|
"m2p",
|
||||||
|
m2it->second.first->GetSourcePath(),
|
||||||
|
buf,
|
||||||
|
ok,
|
||||||
|
ok ? result.size() : 0,
|
||||||
|
static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::microseconds>(
|
||||||
|
std::chrono::steady_clock::now() - loadStart).count()));
|
||||||
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto it = m_entries.find(buf);
|
auto it = m_entries.find(buf);
|
||||||
if (it != m_entries.end()) {
|
if (it != m_entries.end()) {
|
||||||
return it->second.first->GetFileWithPool(it->second.second, result, pPool);
|
const auto loadStart = std::chrono::steady_clock::now();
|
||||||
|
const bool ok = it->second.first->GetFileWithPool(it->second.second, result, pPool);
|
||||||
|
RecordPackProfileLoad(
|
||||||
|
"pck",
|
||||||
|
it->second.first->GetSourcePath(),
|
||||||
|
buf,
|
||||||
|
ok,
|
||||||
|
ok ? result.size() : 0,
|
||||||
|
static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::microseconds>(
|
||||||
|
std::chrono::steady_clock::now() - loadStart).count()));
|
||||||
|
return ok;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback to disk (for files not in packs, like bgm folder)
|
// Fallback to disk (for files not in packs, like bgm folder)
|
||||||
|
const auto loadStart = std::chrono::steady_clock::now();
|
||||||
std::ifstream ifs(buf, std::ios::binary);
|
std::ifstream ifs(buf, std::ios::binary);
|
||||||
if (ifs.is_open()) {
|
if (ifs.is_open()) {
|
||||||
ifs.seekg(0, std::ios::end);
|
ifs.seekg(0, std::ios::end);
|
||||||
@@ -97,10 +151,27 @@ bool CPackManager::GetFileWithPool(std::string_view path, TPackFile& result, CBu
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ifs.read((char*)result.data(), size)) {
|
if (ifs.read((char*)result.data(), size)) {
|
||||||
|
RecordPackProfileLoad(
|
||||||
|
"disk",
|
||||||
|
"<disk>",
|
||||||
|
buf,
|
||||||
|
true,
|
||||||
|
result.size(),
|
||||||
|
static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::microseconds>(
|
||||||
|
std::chrono::steady_clock::now() - loadStart).count()));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RecordPackProfileLoad(
|
||||||
|
"disk",
|
||||||
|
"<disk>",
|
||||||
|
buf,
|
||||||
|
false,
|
||||||
|
0,
|
||||||
|
static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::microseconds>(
|
||||||
|
std::chrono::steady_clock::now() - loadStart).count()));
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
494
src/PackLib/PackProfile.cpp
Normal file
494
src/PackLib/PackProfile.cpp
Normal file
@@ -0,0 +1,494 @@
|
|||||||
|
#include "PackProfile.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
#include <cctype>
|
||||||
|
#include <chrono>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
|
#include <map>
|
||||||
|
#include <mutex>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include "EterBase/Utils.h"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
using Clock = std::chrono::steady_clock;
|
||||||
|
|
||||||
|
constexpr const char* kPackProfileEnv = "M2PACK_PROFILE";
|
||||||
|
constexpr const char* kPackProfileOutput = "log/pack_profile.txt";
|
||||||
|
constexpr std::size_t kTopListLimit = 12;
|
||||||
|
|
||||||
|
struct LoadStats
|
||||||
|
{
|
||||||
|
std::uint64_t count = 0;
|
||||||
|
std::uint64_t failures = 0;
|
||||||
|
std::uint64_t output_bytes = 0;
|
||||||
|
std::uint64_t elapsed_us = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MountStats
|
||||||
|
{
|
||||||
|
std::uint64_t count = 0;
|
||||||
|
std::uint64_t failures = 0;
|
||||||
|
std::uint64_t entry_count = 0;
|
||||||
|
std::uint64_t elapsed_us = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct StageStats
|
||||||
|
{
|
||||||
|
std::uint64_t count = 0;
|
||||||
|
std::uint64_t input_bytes = 0;
|
||||||
|
std::uint64_t output_bytes = 0;
|
||||||
|
std::uint64_t elapsed_us = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PhaseMarker
|
||||||
|
{
|
||||||
|
std::string phase;
|
||||||
|
std::uint64_t elapsed_us = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PackProfileState
|
||||||
|
{
|
||||||
|
bool initialized = false;
|
||||||
|
bool enabled = false;
|
||||||
|
std::string current_phase = "startup";
|
||||||
|
Clock::time_point start_time {};
|
||||||
|
std::vector<PhaseMarker> markers;
|
||||||
|
std::map<std::string, LoadStats> load_by_format;
|
||||||
|
std::map<std::string, LoadStats> load_by_phase_format;
|
||||||
|
std::map<std::string, LoadStats> load_by_phase_pack;
|
||||||
|
std::map<std::string, LoadStats> load_by_extension;
|
||||||
|
std::map<std::string, MountStats> mount_by_format;
|
||||||
|
std::map<std::string, MountStats> mount_by_pack;
|
||||||
|
std::map<std::string, StageStats> stage_by_key;
|
||||||
|
std::mutex mutex;
|
||||||
|
|
||||||
|
~PackProfileState()
|
||||||
|
{
|
||||||
|
if (enabled)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mutex);
|
||||||
|
WriteReportLocked("shutdown");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteReportLocked(std::string_view reason) const
|
||||||
|
{
|
||||||
|
std::error_code ec;
|
||||||
|
std::filesystem::create_directories("log", ec);
|
||||||
|
|
||||||
|
std::ofstream output(kPackProfileOutput, std::ios::binary | std::ios::trunc);
|
||||||
|
if (!output)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
output << "PACK PROFILE REPORT\n";
|
||||||
|
output << "reason=" << reason << "\n";
|
||||||
|
output << "current_phase=" << current_phase << "\n";
|
||||||
|
|
||||||
|
const auto uptime_us = start_time == Clock::time_point {}
|
||||||
|
? 0
|
||||||
|
: static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::microseconds>(
|
||||||
|
Clock::now() - start_time).count());
|
||||||
|
output << "uptime_ms=" << FormatMs(uptime_us) << "\n\n";
|
||||||
|
|
||||||
|
output << "[phase_markers]\n";
|
||||||
|
for (const auto& marker : markers)
|
||||||
|
{
|
||||||
|
output << marker.phase << "\t" << FormatMs(marker.elapsed_us) << " ms\n";
|
||||||
|
}
|
||||||
|
output << "\n";
|
||||||
|
|
||||||
|
WriteMountSection(output, "[mounts_by_format]", mount_by_format);
|
||||||
|
WriteMountSection(output, "[mounts_by_pack]", mount_by_pack, kTopListLimit);
|
||||||
|
WriteLoadSection(output, "[loads_by_format]", load_by_format);
|
||||||
|
WriteLoadSection(output, "[loads_by_phase_format]", load_by_phase_format);
|
||||||
|
WriteLoadSection(output, "[top_phase_pack_loads]", load_by_phase_pack, kTopListLimit);
|
||||||
|
WriteLoadSection(output, "[top_extension_loads]", load_by_extension, kTopListLimit);
|
||||||
|
WriteStageSection(output, "[loader_stages]", stage_by_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string FormatMs(std::uint64_t elapsed_us)
|
||||||
|
{
|
||||||
|
std::ostringstream out;
|
||||||
|
out.setf(std::ios::fixed, std::ios::floatfield);
|
||||||
|
out.precision(3);
|
||||||
|
out << (static_cast<double>(elapsed_us) / 1000.0);
|
||||||
|
return out.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string ToLowerAscii(std::string value)
|
||||||
|
{
|
||||||
|
std::transform(value.begin(), value.end(), value.begin(), [](unsigned char ch) {
|
||||||
|
return static_cast<char>(std::tolower(ch));
|
||||||
|
});
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string ParsePackLabel(std::string_view pack_path)
|
||||||
|
{
|
||||||
|
const std::filesystem::path path { std::string(pack_path) };
|
||||||
|
const auto filename = path.filename().string();
|
||||||
|
if (!filename.empty())
|
||||||
|
{
|
||||||
|
return ToLowerAscii(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ToLowerAscii(std::string(pack_path));
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string ParseExtension(std::string_view request_path)
|
||||||
|
{
|
||||||
|
const std::filesystem::path path { std::string(request_path) };
|
||||||
|
auto extension = path.extension().string();
|
||||||
|
if (extension.empty())
|
||||||
|
{
|
||||||
|
return "<none>";
|
||||||
|
}
|
||||||
|
|
||||||
|
return ToLowerAscii(std::move(extension));
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string MakePhaseKey(std::string_view phase, std::string_view suffix)
|
||||||
|
{
|
||||||
|
std::string key;
|
||||||
|
key.reserve(phase.size() + suffix.size() + 3);
|
||||||
|
key.append(phase);
|
||||||
|
key.append(" | ");
|
||||||
|
key.append(suffix);
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename MapType>
|
||||||
|
static std::vector<std::pair<std::string, typename MapType::mapped_type>> SortByTimeDesc(const MapType& values)
|
||||||
|
{
|
||||||
|
using StatsType = typename MapType::mapped_type;
|
||||||
|
std::vector<std::pair<std::string, StatsType>> sorted;
|
||||||
|
sorted.reserve(values.size());
|
||||||
|
for (const auto& [key, stats] : values)
|
||||||
|
{
|
||||||
|
sorted.emplace_back(key, stats);
|
||||||
|
}
|
||||||
|
std::sort(sorted.begin(), sorted.end(), [](const auto& left, const auto& right) {
|
||||||
|
if (left.second.elapsed_us != right.second.elapsed_us)
|
||||||
|
{
|
||||||
|
return left.second.elapsed_us > right.second.elapsed_us;
|
||||||
|
}
|
||||||
|
|
||||||
|
return left.first < right.first;
|
||||||
|
});
|
||||||
|
return sorted;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void WriteLoadSection(std::ofstream& output, std::string_view header, const std::map<std::string, LoadStats>& values, std::size_t limit = 0)
|
||||||
|
{
|
||||||
|
output << header << "\n";
|
||||||
|
const auto sorted = SortByTimeDesc(values);
|
||||||
|
const std::size_t count = limit == 0 ? sorted.size() : std::min(limit, sorted.size());
|
||||||
|
for (std::size_t i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
const auto& [key, stats] = sorted[i];
|
||||||
|
output
|
||||||
|
<< key
|
||||||
|
<< "\tcount=" << stats.count
|
||||||
|
<< "\tfail=" << stats.failures
|
||||||
|
<< "\tbytes=" << stats.output_bytes
|
||||||
|
<< "\ttime_ms=" << FormatMs(stats.elapsed_us)
|
||||||
|
<< "\n";
|
||||||
|
}
|
||||||
|
output << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
static void WriteMountSection(std::ofstream& output, std::string_view header, const std::map<std::string, MountStats>& values, std::size_t limit = 0)
|
||||||
|
{
|
||||||
|
output << header << "\n";
|
||||||
|
std::vector<std::pair<std::string, MountStats>> sorted(values.begin(), values.end());
|
||||||
|
std::sort(sorted.begin(), sorted.end(), [](const auto& left, const auto& right) {
|
||||||
|
if (left.second.elapsed_us != right.second.elapsed_us)
|
||||||
|
{
|
||||||
|
return left.second.elapsed_us > right.second.elapsed_us;
|
||||||
|
}
|
||||||
|
|
||||||
|
return left.first < right.first;
|
||||||
|
});
|
||||||
|
|
||||||
|
const std::size_t count = limit == 0 ? sorted.size() : std::min(limit, sorted.size());
|
||||||
|
for (std::size_t i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
const auto& [key, stats] = sorted[i];
|
||||||
|
output
|
||||||
|
<< key
|
||||||
|
<< "\tcount=" << stats.count
|
||||||
|
<< "\tfail=" << stats.failures
|
||||||
|
<< "\tentries=" << stats.entry_count
|
||||||
|
<< "\ttime_ms=" << FormatMs(stats.elapsed_us)
|
||||||
|
<< "\n";
|
||||||
|
}
|
||||||
|
output << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
static void WriteStageSection(std::ofstream& output, std::string_view header, const std::map<std::string, StageStats>& values)
|
||||||
|
{
|
||||||
|
output << header << "\n";
|
||||||
|
std::vector<std::pair<std::string, StageStats>> sorted(values.begin(), values.end());
|
||||||
|
std::sort(sorted.begin(), sorted.end(), [](const auto& left, const auto& right) {
|
||||||
|
if (left.second.elapsed_us != right.second.elapsed_us)
|
||||||
|
{
|
||||||
|
return left.second.elapsed_us > right.second.elapsed_us;
|
||||||
|
}
|
||||||
|
|
||||||
|
return left.first < right.first;
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const auto& [key, stats] : sorted)
|
||||||
|
{
|
||||||
|
output
|
||||||
|
<< key
|
||||||
|
<< "\tcount=" << stats.count
|
||||||
|
<< "\tin=" << stats.input_bytes
|
||||||
|
<< "\tout=" << stats.output_bytes
|
||||||
|
<< "\ttime_ms=" << FormatMs(stats.elapsed_us)
|
||||||
|
<< "\n";
|
||||||
|
}
|
||||||
|
output << "\n";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
PackProfileState g_pack_profile;
|
||||||
|
|
||||||
|
bool ParseBoolFlag(std::string value, bool& out)
|
||||||
|
{
|
||||||
|
value.erase(std::remove_if(value.begin(), value.end(), [](unsigned char ch) {
|
||||||
|
return std::isspace(ch) != 0;
|
||||||
|
}), value.end());
|
||||||
|
|
||||||
|
std::transform(value.begin(), value.end(), value.begin(), [](unsigned char ch) {
|
||||||
|
return static_cast<char>(std::tolower(ch));
|
||||||
|
});
|
||||||
|
|
||||||
|
if (value == "1" || value == "true" || value == "yes" || value == "on")
|
||||||
|
{
|
||||||
|
out = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value == "0" || value == "false" || value == "no" || value == "off")
|
||||||
|
{
|
||||||
|
out = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GetEnvString(const char* name)
|
||||||
|
{
|
||||||
|
char buffer[256];
|
||||||
|
const DWORD len = GetEnvironmentVariableA(name, buffer, sizeof(buffer));
|
||||||
|
if (len == 0 || len >= sizeof(buffer))
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::string(buffer, buffer + len);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint64_t CurrentElapsedUs()
|
||||||
|
{
|
||||||
|
if (!g_pack_profile.enabled)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::microseconds>(
|
||||||
|
Clock::now() - g_pack_profile.start_time).count());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void InitializePackProfile(const char* commandLine)
|
||||||
|
{
|
||||||
|
if (g_pack_profile.initialized)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_pack_profile.initialized = true;
|
||||||
|
g_pack_profile.enabled = false;
|
||||||
|
|
||||||
|
const std::string envProfile = GetEnvString(kPackProfileEnv);
|
||||||
|
if (!envProfile.empty())
|
||||||
|
{
|
||||||
|
bool enabled = false;
|
||||||
|
if (ParseBoolFlag(envProfile, enabled))
|
||||||
|
{
|
||||||
|
g_pack_profile.enabled = enabled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int argc = 0;
|
||||||
|
PCHAR* argv = CommandLineToArgv(const_cast<PCHAR>(commandLine ? commandLine : ""), &argc);
|
||||||
|
if (argv)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < argc; ++i)
|
||||||
|
{
|
||||||
|
const std::string arg = argv[i];
|
||||||
|
if (arg != "--m2pack-profile")
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool enabled = true;
|
||||||
|
if (i + 1 < argc)
|
||||||
|
{
|
||||||
|
const std::string next = argv[i + 1];
|
||||||
|
if (!next.starts_with("--") && ParseBoolFlag(next, enabled))
|
||||||
|
{
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g_pack_profile.enabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
SAFE_FREE_GLOBAL(argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!g_pack_profile.enabled)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(g_pack_profile.mutex);
|
||||||
|
g_pack_profile.current_phase = "startup";
|
||||||
|
g_pack_profile.start_time = Clock::now();
|
||||||
|
g_pack_profile.markers.push_back(PhaseMarker {
|
||||||
|
.phase = g_pack_profile.current_phase,
|
||||||
|
.elapsed_us = 0,
|
||||||
|
});
|
||||||
|
g_pack_profile.WriteReportLocked("initialized");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsPackProfileEnabled()
|
||||||
|
{
|
||||||
|
return g_pack_profile.enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MarkPackProfilePhase(std::string_view phase)
|
||||||
|
{
|
||||||
|
if (!g_pack_profile.enabled)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(g_pack_profile.mutex);
|
||||||
|
if (g_pack_profile.current_phase == phase)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_pack_profile.current_phase = std::string(phase);
|
||||||
|
g_pack_profile.markers.push_back(PhaseMarker {
|
||||||
|
.phase = g_pack_profile.current_phase,
|
||||||
|
.elapsed_us = CurrentElapsedUs(),
|
||||||
|
});
|
||||||
|
g_pack_profile.WriteReportLocked(PackProfileState::MakePhaseKey("phase", phase));
|
||||||
|
}
|
||||||
|
|
||||||
|
void RecordPackProfileMount(
|
||||||
|
std::string_view format,
|
||||||
|
std::string_view packPath,
|
||||||
|
bool ok,
|
||||||
|
std::size_t entryCount,
|
||||||
|
std::uint64_t elapsedUs)
|
||||||
|
{
|
||||||
|
if (!g_pack_profile.enabled)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(g_pack_profile.mutex);
|
||||||
|
|
||||||
|
auto& formatStats = g_pack_profile.mount_by_format[std::string(format)];
|
||||||
|
formatStats.count += 1;
|
||||||
|
formatStats.failures += ok ? 0 : 1;
|
||||||
|
formatStats.entry_count += entryCount;
|
||||||
|
formatStats.elapsed_us += elapsedUs;
|
||||||
|
|
||||||
|
auto& packStats = g_pack_profile.mount_by_pack[PackProfileState::ParsePackLabel(packPath)];
|
||||||
|
packStats.count += 1;
|
||||||
|
packStats.failures += ok ? 0 : 1;
|
||||||
|
packStats.entry_count += entryCount;
|
||||||
|
packStats.elapsed_us += elapsedUs;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RecordPackProfileLoad(
|
||||||
|
std::string_view format,
|
||||||
|
std::string_view packPath,
|
||||||
|
std::string_view requestPath,
|
||||||
|
bool ok,
|
||||||
|
std::uint64_t outputBytes,
|
||||||
|
std::uint64_t elapsedUs)
|
||||||
|
{
|
||||||
|
if (!g_pack_profile.enabled)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(g_pack_profile.mutex);
|
||||||
|
|
||||||
|
auto update = [ok, outputBytes, elapsedUs](LoadStats& stats) {
|
||||||
|
stats.count += 1;
|
||||||
|
stats.failures += ok ? 0 : 1;
|
||||||
|
stats.output_bytes += outputBytes;
|
||||||
|
stats.elapsed_us += elapsedUs;
|
||||||
|
};
|
||||||
|
|
||||||
|
update(g_pack_profile.load_by_format[std::string(format)]);
|
||||||
|
update(g_pack_profile.load_by_phase_format[
|
||||||
|
PackProfileState::MakePhaseKey(g_pack_profile.current_phase, format)]);
|
||||||
|
update(g_pack_profile.load_by_phase_pack[
|
||||||
|
PackProfileState::MakePhaseKey(g_pack_profile.current_phase, PackProfileState::ParsePackLabel(packPath))]);
|
||||||
|
update(g_pack_profile.load_by_extension[PackProfileState::ParseExtension(requestPath)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RecordPackProfileStage(
|
||||||
|
std::string_view format,
|
||||||
|
std::string_view stage,
|
||||||
|
std::uint64_t inputBytes,
|
||||||
|
std::uint64_t outputBytes,
|
||||||
|
std::uint64_t elapsedUs)
|
||||||
|
{
|
||||||
|
if (!g_pack_profile.enabled)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(g_pack_profile.mutex);
|
||||||
|
auto& stats = g_pack_profile.stage_by_key[
|
||||||
|
PackProfileState::MakePhaseKey(format, stage)];
|
||||||
|
stats.count += 1;
|
||||||
|
stats.input_bytes += inputBytes;
|
||||||
|
stats.output_bytes += outputBytes;
|
||||||
|
stats.elapsed_us += elapsedUs;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlushPackProfileSnapshot(std::string_view reason)
|
||||||
|
{
|
||||||
|
if (!g_pack_profile.enabled)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(g_pack_profile.mutex);
|
||||||
|
g_pack_profile.WriteReportLocked(reason);
|
||||||
|
}
|
||||||
29
src/PackLib/PackProfile.h
Normal file
29
src/PackLib/PackProfile.h
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
void InitializePackProfile(const char* commandLine);
|
||||||
|
bool IsPackProfileEnabled();
|
||||||
|
void MarkPackProfilePhase(std::string_view phase);
|
||||||
|
void RecordPackProfileMount(
|
||||||
|
std::string_view format,
|
||||||
|
std::string_view packPath,
|
||||||
|
bool ok,
|
||||||
|
std::size_t entryCount,
|
||||||
|
std::uint64_t elapsedUs);
|
||||||
|
void RecordPackProfileLoad(
|
||||||
|
std::string_view format,
|
||||||
|
std::string_view packPath,
|
||||||
|
std::string_view requestPath,
|
||||||
|
bool ok,
|
||||||
|
std::uint64_t outputBytes,
|
||||||
|
std::uint64_t elapsedUs);
|
||||||
|
void RecordPackProfileStage(
|
||||||
|
std::string_view format,
|
||||||
|
std::string_view stage,
|
||||||
|
std::uint64_t inputBytes,
|
||||||
|
std::uint64_t outputBytes,
|
||||||
|
std::uint64_t elapsedUs);
|
||||||
|
void FlushPackProfileSnapshot(std::string_view reason);
|
||||||
@@ -20,6 +20,7 @@
|
|||||||
#include "PythonApplication.h"
|
#include "PythonApplication.h"
|
||||||
|
|
||||||
#include "GameLib/ItemManager.h"
|
#include "GameLib/ItemManager.h"
|
||||||
|
#include "PackLib/PackProfile.h"
|
||||||
|
|
||||||
#include "AbstractApplication.h"
|
#include "AbstractApplication.h"
|
||||||
#include "AbstractCharacterManager.h"
|
#include "AbstractCharacterManager.h"
|
||||||
@@ -414,6 +415,7 @@ void CPythonNetworkStream::SetGamePhase()
|
|||||||
if ("Game"!=m_strPhase)
|
if ("Game"!=m_strPhase)
|
||||||
m_phaseLeaveFunc.Run();
|
m_phaseLeaveFunc.Run();
|
||||||
|
|
||||||
|
MarkPackProfilePhase("game");
|
||||||
m_strPhase = "Game";
|
m_strPhase = "Game";
|
||||||
|
|
||||||
m_dwChangingPhaseTime = ELTimer_GetMSec();
|
m_dwChangingPhaseTime = ELTimer_GetMSec();
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include "NetworkActorManager.h"
|
#include "NetworkActorManager.h"
|
||||||
#include "AbstractPlayer.h"
|
#include "AbstractPlayer.h"
|
||||||
#include "PackLib/PackManager.h"
|
#include "PackLib/PackManager.h"
|
||||||
|
#include "PackLib/PackProfile.h"
|
||||||
|
|
||||||
void CPythonNetworkStream::EnableChatInsultFilter(bool isEnable)
|
void CPythonNetworkStream::EnableChatInsultFilter(bool isEnable)
|
||||||
{
|
{
|
||||||
@@ -90,6 +91,7 @@ void CPythonNetworkStream::SetLoadingPhase()
|
|||||||
Tracen("");
|
Tracen("");
|
||||||
Tracen("## Network - Loading Phase ##");
|
Tracen("## Network - Loading Phase ##");
|
||||||
Tracen("");
|
Tracen("");
|
||||||
|
MarkPackProfilePhase("loading");
|
||||||
|
|
||||||
m_strPhase = "Loading";
|
m_strPhase = "Loading";
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#include "PythonNetworkStream.h"
|
#include "PythonNetworkStream.h"
|
||||||
#include "Packet.h"
|
#include "Packet.h"
|
||||||
#include "AccountConnector.h"
|
#include "AccountConnector.h"
|
||||||
|
#include "PackLib/PackProfile.h"
|
||||||
|
|
||||||
// Login ---------------------------------------------------------------------------
|
// Login ---------------------------------------------------------------------------
|
||||||
void CPythonNetworkStream::LoginPhase()
|
void CPythonNetworkStream::LoginPhase()
|
||||||
@@ -18,6 +19,7 @@ void CPythonNetworkStream::SetLoginPhase()
|
|||||||
Tracen("");
|
Tracen("");
|
||||||
Tracen("## Network - Login Phase ##");
|
Tracen("## Network - Login Phase ##");
|
||||||
Tracen("");
|
Tracen("");
|
||||||
|
MarkPackProfilePhase("login");
|
||||||
|
|
||||||
m_strPhase = "Login";
|
m_strPhase = "Login";
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user