在 Manus 中运行任何 Skill
一键导入
一键导入
一键在 Manus 中运行任何 Skill
开始使用mc-add-trace
星标2
分支0
更新时间2026年7月10日 04:47
为用户指定的代码范围增加性能追踪宏(Perfetto + Tracy 双轨)
安装
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
SKILL.md
readonly菜单
为用户指定的代码范围增加性能追踪宏(Perfetto + Tracy 双轨)
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
| name | mc-add-trace |
| description | 为用户指定的代码范围增加性能追踪宏(Perfetto + Tracy 双轨) |
为用户指定的代码范围增加性能追踪宏插桩。项目采用 Perfetto + Tracy 双轨:MC_TRACE_* 宏会同时向两套后端发事件(除非编译时关闭其一),对外 API 不区分后端。下面是一些最佳实践:
std::string ResourcePackList::normalizePath(const std::filesystem::path& path)
{
MC_TRACE_SCOPED_EVENT(TraceEvents.Client.Resource, "ResourcePackList::normalizePath");
std::string result = path.string();
// 统一使用正斜杠
std::replace(result.begin(), result.end(), '\\', '/');
return result;
}
void NetworkClient::handleBlockUpdate(network::PacketDeserializer& deser)
{
auto result = network::BlockUpdatePacket::deserialize(deser);
if (result.failed()) {
spdlog::error("[NetworkClient::handleBlockUpdate] Failed to deserialize block update packet");
return;
}
auto& packet = result.value();
MC_TRACE_INSTANT_EVENT(TraceEvents.Client.Lighting,
"ReceiveBlockUpdate",
"pos",
fmt::format("({}, {}, {})", packet.x(), packet.y(), packet.z()),
"stateId",
packet.blockStateId(),
[flow = ::perfetto::Flow::ProcessScoped(BlockPos(packet.x(), packet.y(), packet.z()).toId())](
::perfetto::EventContext ctx) { flow(ctx); });
if (m_callbacks.onBlockUpdate) {
m_callbacks.onBlockUpdate(packet.x(), packet.y(), packet.z(), packet.blockStateId());
}
}
void ClientWorld::onLightUpdate(i32 chunkX,
i32 chunkZ,
i32 sectionY,
const std::vector<u8>& skyLight,
const std::vector<u8>& blockLight,
bool /*trustEdges*/
)
{
MC_TRACE_SCOPED_EVENT(TraceEvents.Client.Lighting,
"ClientWorld::onLightUpdate",
"Section",
fmt::format("({}, {}, {})", chunkX, sectionY, chunkZ),
"SkyLightSize",
skyLight.size(),
"BlockLightSize",
blockLight.size(),
[flow = ::perfetto::Flow::ProcessScoped(SectionPos(chunkX, sectionY, chunkZ).toLong())](
::perfetto::EventContext ctx) { flow(ctx); });
const ChunkId id(chunkX, chunkZ);
ClientChunk* chunk = getChunk(id);
if (!chunk || !chunk->data) {
return;
}
ChunkSection* section = chunk->data->getSection(sectionY);
if (!section) {
return;
}
if (!skyLight.empty() && skyLight.size() == NibbleArray::BYTE_SIZE) {
section->skyLightNibble() = NibbleArray(skyLight);
}
if (!blockLight.empty() && blockLight.size() == NibbleArray::BYTE_SIZE) {
section->blockLightNibble() = NibbleArray(blockLight);
}
requestChunkMeshRebuild(id);
}
void MinecraftServer::initializeRegistries(bool registerEntities)
{
MC_TRACE_SCOPED_EVENT(TraceEvents.Server.Initialization, "MinecraftServer::initializeRegistries");
// 注意,下面加trace的方式不推荐,建议进入相关函数,然后在函数内部顶部加trace,这样能避免外层函数全是trace,影响可读性。除非相关逻辑没有被封装为函数,时才可以在外层函数加trace。
// 初始化方块注册表
{
MC_TRACE_SCOPED_EVENT(TraceEvents.Server.Initialization, "MinecraftServer::initializeRegistries::Blocks");
VanillaBlocks::initialize();
}
spdlog::info("Vanilla blocks initialized");
// 初始化物品注册表
{
MC_TRACE_SCOPED_EVENT(TraceEvents.Server.Initialization, "MinecraftServer::initializeRegistries::Items");
Items::initialize();
}
spdlog::info("Vanilla items initialized");
// 初始化附魔注册表
{
MC_TRACE_SCOPED_EVENT(TraceEvents.Server.Initialization, "MinecraftServer::initializeRegistries::Enchantments");
item::enchant::EnchantmentRegistry::initialize();
}
spdlog::info("Enchantments initialized");
// 初始化方块物品注册表
{
MC_TRACE_SCOPED_EVENT(TraceEvents.Server.Initialization, "MinecraftServer::initializeRegistries::BlockItems");
BlockItemRegistry::instance().initializeVanillaBlockItems();
}
spdlog::info("Block items initialized");
// 初始化物品标签(必须在所有物品注册后)
{
MC_TRACE_SCOPED_EVENT(TraceEvents.Server.Initialization, "MinecraftServer::initializeRegistries::ItemTags");
item::tag::ItemTags::initialize();
}
spdlog::info("Item tags initialized");
// 初始化发射器行为注册表
{
MC_TRACE_SCOPED_EVENT(TraceEvents.Server.Initialization, "MinecraftServer::initializeRegistries::DispenseBehaviors");
blocks::DispenseItemBehaviorRegistry::instance().initDefaultBehaviors();
}
spdlog::info("Dispense item behaviors initialized");
}
mc::trace::TraceEvents 枚举树(定义在 src\common\profiler\TraceCategories.hpp)。该枚举树按子系统组织成嵌套结构体,叶子节点形如 TraceEvents.Server.Tick、TraceEvents.Rendering.Frame、TraceEvents.World.ChunkGen。尽可能复用已有的叶子节点,非必要不要新增;新增时须同时在枚举树和 PERFETTO_DEFINE_CATEGORIES 中登记(仅 Perfetto 后端需要注册,Tracy 不消费 category)。使用枚举树之外的类别会导致编译失败(仅 Perfetto 启用时)。类名::函数名的格式。perfetto::EventContext参数,用户可以在lambda中使用这个参数来触发Flow事件。Flow事件可以用来关联不同函数中的事件,方便在perfetto UI中分析调用关系。如果你追踪的函数参数中有可以用来唯一标识调用实例的信息(比如实体ID、区块位置、方块Pos等),建议使用这些信息来构造Flow ID,这样可以在perfetto UI中清晰地看到同一个实体或区块的事件是如何流转的。MC_TRACE_SCOPED_EVENT 而不是 MC_TRACE_INSTANT_EVENT,因为前者可以记录一个时间区间,而后者只能记录一瞬间,导致信息量减少,除非你确实只关心一个瞬间事件(比如用户按下键盘、收到网络包),否则建议使用 MC_TRACE_SCOPED_EVENT。#include "common/profiler/TraceEvents.hpp" 如果没有包含,则需要增加这个包含语句。.cpp 文件通常在 include 区之后加一行 using namespace mc::trace;,即可直接写 TraceEvents.Server.Tick;.hpp 文件用全限定 ::mc::trace::TraceEvents.X.Y,不要在头文件加 using namespace。MC_TRACE_SCOPED_EVENT(category, name, ...) — 作用域事件(最常用,RAII 自动结束)MC_TRACE_INSTANT_EVENT(category, name, ...) — 瞬时事件(零持续时间)MC_TRACE_COUNTER(category, name, value) — 计数器(value 须为 int64_t)MC_TRACE_EVENT_BEGIN(category, name, ...) / MC_TRACE_EVENT_END(category) — 手动跨函数配对MC_TRACE_SET_THREAD_NAME(name) — 线程命名开始修改代码之前,请先列出自己的修改计划,说明你打算在哪些文件的哪些函数中添加追踪宏,以及每个事件用哪个枚举树叶节点。
代码编写结束之后,必须使用clang-format对你修改的文件进行格式化:
clang-format -i src\common\xxx\Foo.cpp
clang-format -i src\common\xxx\Foo.hpp
然后进行编译。
运行测试,认领并收敛项目代码中的一个测试错误
认领并收敛项目代码中的一个TODO
基于测试驱动的开发(TDD),将当前项目的代码与 Minecraft 原版代码对齐,确保功能和逻辑都能准确复刻 Minecraft。
审查用户指定范围内的所有文件,列出架构重构计划并执行
参照mc源码,列出高质量计划,为项目实现新功能
审查用户指定范围内的文件,在保持逻辑大体不变的情况下,删除、简化不必要的内容,使其更清晰易懂。