载入中...
搜索中...
未找到
PlayTrace.cpp
浏览该文件的文档.
2
3#include <cstdio>
4#include <set>
5#include <utility>
6
7namespace eve::dev {
8namespace {
9
10constexpr std::string_view kTraceSchemaId = "evengine.play-trace";
11constexpr std::int64_t kTraceVersion = 1;
12
13Result<void> knownFields(const Value::Object& object, const std::set<std::string>& allowed,
14 std::string_view path) {
15 for (const auto& [name, value] : object) {
16 (void)value;
17 if (!allowed.contains(name))
19 std::string(path) + "." + name));
20 }
21 return Result<void>::success();
22}
23
24Result<const Value::Object*> asObject(const Value& value, std::string path) {
25 const auto* object = value.getIf<Value::Object>();
26 if (!object)
28 Diagnostic::error(DiagnosticCode::ParseError, "play-trace value must be an object", path));
30}
31
32std::string hex64(std::uint64_t value) {
33 char buffer[17];
34 std::snprintf(buffer, sizeof(buffer), "%016llx", static_cast<unsigned long long>(value));
35 return buffer;
36}
37
38std::uint64_t fnv1a64(std::string_view text) {
39 std::uint64_t hash = 14695981039346656037ull;
40 for (unsigned char byte : text) {
41 hash ^= byte;
42 hash *= 1099511628211ull;
43 }
44 return hash;
45}
46
47Result<std::string> stringField(const Value::Object& object, std::string_view name, std::string_view path) {
48 const auto found = object.find(std::string(name));
49 if (found == object.end() || !found->second.isString())
51 std::string(path) + "." + std::string(name)));
52 return Result<std::string>::success(found->second.asString());
53}
54
55} // namespace
56
58 auto json = state.toJson();
59 if (!json) return Result<std::string>::failure(json.status());
60 return Result<std::string>::success("fnv1a64:" + hex64(fnv1a64(json.value())));
61}
62
64 auto root = asObject(recording, "recording");
65 if (!root) return Result<Value>::failure(root.status());
66 auto known = knownFields(*root.value(),
67 {"contractHash", "contractId", "engine", "result", "schemaId", "schemaVersion", "seed",
68 "startCheckpoint", "steps"},
69 "recording");
70 if (!known) return Result<Value>::failure(known.status());
71 auto schema = stringField(*root.value(), "schemaId", "recording");
72 if (!schema) return Result<Value>::failure(schema.status());
73 const auto version = root.value()->find("schemaVersion");
74 if (version == root.value()->end() || !version->second.isInt64() || version->second.asInt() != kTraceVersion)
76 Diagnostic::error(DiagnosticCode::Unsupported, "unsupported play-trace schema", "recording.schemaVersion"));
77 if (schema.value() != kTraceSchemaId)
79 Diagnostic::error(DiagnosticCode::Unsupported, "unsupported play-trace schema", "recording.schemaId"));
80 const auto steps = root.value()->find("steps");
81 if (steps == root.value()->end() || !steps->second.isArray())
83 Diagnostic::error(DiagnosticCode::ParseError, "play-trace requires steps", "recording.steps"));
84 const auto* array = steps->second.getIf<Value::Array>();
85 for (std::size_t index = 0; index < array->size(); ++index) {
86 const std::string itemPath = "recording.steps." + std::to_string(index);
87 auto item = asObject((*array)[index], itemPath);
88 if (!item) return Result<Value>::failure(item.status());
89 auto itemKnown =
90 knownFields(*item.value(), {"capture", "hostFrame", "observationDigest", "request", "seq"}, itemPath);
91 if (!itemKnown) return Result<Value>::failure(itemKnown.status());
92 const auto request = item.value()->find("request");
93 if (request == item.value()->end())
95 Diagnostic::error(DiagnosticCode::ParseError, "trace step requires request", itemPath + ".request"));
96 }
97 return Result<Value>::success(recording);
98}
99
102
104 auto parsed = parsePlayTrace(recording);
105 if (!parsed) return Result<Value>::failure(parsed.status());
106 auto root = asObject(parsed.value(), "recording");
107 if (!root) return Result<Value>::failure(root.status());
108 const auto checkpoint = root.value()->find("startCheckpoint");
109 if (checkpoint != root.value()->end() && checkpoint->second.isString() &&
110 !checkpoint->second.asString().empty()) {
111 auto restored = runtime.restoreCheckpoint(checkpoint->second.asString());
112 if (!restored) return Result<Value>::failure(restored.status());
113 }
114 PlayReplayGuard guard;
115 const auto* steps = root.value()->find("steps")->second.getIf<Value::Array>();
116 for (std::size_t index = 0; index < steps->size(); ++index) {
117 const auto* step = (*steps)[index].getIf<Value::Object>();
118 if (!step)
119 return Result<Value>::failure(Diagnostic::error(DiagnosticCode::ParseError, "trace step must be an object",
120 "recording.steps." + std::to_string(index)));
121 auto replayed = executePlayRequest(step->at("request"), runtime);
122 if (!replayed) return Result<Value>::failure(replayed.status());
123 const auto digest = step->find("observationDigest");
124 if (digest != step->end() && digest->second.isString() && !digest->second.asString().empty()) {
125 const auto* response = replayed.value().getIf<Value::Object>();
126 if (!response)
128 "replay response must be an object",
129 "recording.steps." + std::to_string(index)));
130 const auto state = response->find("state");
131 if (state == response->end())
133 "replay observe is missing state",
134 "recording.steps." + std::to_string(index)));
135 auto actual = playObservationDigest(state->second);
136 if (!actual) return Result<Value>::failure(actual.status());
137 if (actual.value() != digest->second.asString())
139 "play-trace observation digest mismatch",
140 "recording.steps." + std::to_string(index)));
141 }
142 }
144 {"op", Value("replay")},
145 {"schemaId", Value("evengine.play-response")},
146 {"schemaVersion", Value(std::int64_t{1})},
147 {"status", Value("passed")},
148 {"steps", Value(static_cast<std::int64_t>(steps->size()))},
149 }));
150}
151
156
158 contractId_.clear();
159 contractHash_.clear();
160 seed_ = 0;
161 startCheckpoint_.clear();
162 stepItems_.clear();
163 stepCount_ = 0;
164 begun_ = false;
165}
166
167void PlayTraceBuffer::begin(std::string contractId, std::string contractHash, std::int64_t seed,
168 std::string startCheckpoint) {
169 clear();
170 contractId_ = std::move(contractId);
171 contractHash_ = std::move(contractHash);
172 seed_ = seed;
173 startCheckpoint_ = std::move(startCheckpoint);
174 begun_ = true;
175}
176
177void PlayTraceBuffer::append(const Value& request, const Value& response, std::uint64_t hostFrame) {
178 if (replaying_) return;
179 const auto* object = request.getIf<Value::Object>();
180 if (!object) return;
181 const auto op = object->find("op");
182 if (op == object->end() || !op->second.isString()) return;
183 const std::string& name = op->second.asString();
184 if (name == "status" || name == "clock" || name == "trace" || name == "replay") return;
185 if (!begun_) begin({}, {}, 0, {});
186 Value::Object step{{"hostFrame", Value(static_cast<std::int64_t>(hostFrame))},
187 {"request", request},
188 {"seq", Value(++stepCount_)}};
189 const auto* responseObject = response.getIf<Value::Object>();
190 if (responseObject) {
191 const auto state = responseObject->find("state");
192 if (state != responseObject->end()) {
193 auto digest = playObservationDigest(state->second);
194 if (digest) step.emplace("observationDigest", Value(std::move(digest).takeValue()));
195 }
196 const auto path = responseObject->find("path");
197 if (path != responseObject->end() && path->second.isString())
198 step.emplace("capture", Value(path->second.asString()));
199 }
200 stepItems_.emplace_back(Value(std::move(step)));
201}
202
204 return Value(Value::Object{{"contractHash", Value(contractHash_)},
205 {"contractId", Value(contractId_)},
206 {"schemaId", Value(std::string(kTraceSchemaId))},
207 {"schemaVersion", Value(kTraceVersion)},
208 {"seed", Value(seed_)},
209 {"startCheckpoint", Value(startCheckpoint_)},
210 {"steps", Value(stepItems_)}});
211}
212
213} // namespace eve::dev
double value
int root
Definition AnimSmr.cpp:119
std::array< std::uint8_t, 32 > hash
Definition Evpack.cpp:172
const GltfImportRequest & request
std::string text
std::string name
std::unique_ptr< gpgpu::GpuBuffer > buffer
Definition OnnxGpgpu.cpp:26
std::string path
Definition PlayHost.cpp:110
Versioned Play Trace recording, digest and replay.
std::uint32_t seed
Definition PointSet.cpp:807
float begin
int steps
bool found
std::string digest
Json object
float step
Definition TreeMesh.cpp:314
uint32_t index
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Definition Diagnostic.h:125
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result success(T value)
Construct a successful result owning value.
Definition Result.h:164
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
The canonical owning dynamic value used by data-facing protocols.
Definition Value.h:31
std::map< std::string, Value > Object
Definition Value.h:34
std::vector< Value > Array
Definition Value.h:33
const T * getIf() const noexcept
Return a typed pointer, or nullptr when the kind differs.
Definition Value.h:180
Injected runtime used by Play Host. Tests supply a fake; MCP uses the engine adapter....
Definition PlayHost.h:25
RAII flag so nested Play requests during replay are not recorded.
Definition PlayTrace.h:42
~PlayReplayGuard()
Play replay guard.
PlayReplayGuard()
Play replay guard.
Process-owned append buffer for trace=append.
Definition PlayTrace.h:58
void begin(std::string contractId, std::string contractHash, std::int64_t seed, std::string startCheckpoint)
Begins .
static PlayTraceBuffer & instance()
Instance.
void append(const Value &request, const Value &response, std::uint64_t hostFrame)
Append.
void setReplaying(bool value)
Definition PlayTrace.h:67
Value exportTrace() const
Export trace.
Result< Value > parsePlayTrace(const Value &recording)
Parse and reject unknown fields on a Play Trace recording.
Definition PlayTrace.cpp:63
Result< Value > replayPlayTrace(const Value &recording, IPlayHostRuntime &runtime)
Replay a parsed recording against a host runtime.
Result< std::string > playObservationDigest(const Value &state)
Canonical digest of a Play observation state object.
Definition PlayTrace.cpp:57
Result< Value > executePlayRequest(const Value &request, IPlayHostRuntime &runtime)
Execute one versioned Play request against an injected runtime.
Definition PlayHost.cpp:858