1 // Copyright (C) 2019 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 #include "VkReconstruction.h"
15
16 #include <string.h>
17
18 #include <unordered_map>
19
20 #include "FrameBuffer.h"
21 #include "render-utils/IOStream.h"
22 #include "VkDecoder.h"
23 #include "aemu/base/containers/EntityManager.h"
24
25 namespace gfxstream {
26 namespace vk {
27
28 #define DEBUG_RECONSTRUCTION 0
29
30 #if DEBUG_RECONSTRUCTION
31
32 #define DEBUG_RECON(fmt, ...) fprintf(stderr, "%s:%d " fmt "\n", __func__, __LINE__, ##__VA_ARGS__);
33
34 #else
35
36 #define DEBUG_RECON(fmt, ...)
37
38 #endif
39
40 VkReconstruction::VkReconstruction() = default;
41
typeTagSortedHandles(const std::vector<VkReconstruction::HandleWithState> & handles)42 std::vector<VkReconstruction::HandleWithState> typeTagSortedHandles(
43 const std::vector<VkReconstruction::HandleWithState>& handles) {
44 using EntityManagerTypeForHandles = android::base::EntityManager<32, 16, 16, int>;
45
46 std::vector<VkReconstruction::HandleWithState> res = handles;
47
48 std::sort(res.begin(), res.end(),
49 [](const VkReconstruction::HandleWithState& lhs,
50 const VkReconstruction::HandleWithState& rhs) {
51 if (lhs.second != rhs.second) {
52 return lhs.second < rhs.second;
53 }
54 return EntityManagerTypeForHandles::getHandleType(lhs.first) <
55 EntityManagerTypeForHandles::getHandleType(rhs.first);
56 });
57
58 return res;
59 }
60
save(android::base::Stream * stream)61 void VkReconstruction::save(android::base::Stream* stream) {
62 DEBUG_RECON("start")
63
64 #if DEBUG_RECONSTRUCTION
65 dump();
66 #endif
67
68 std::unordered_map<HandleWithState, int, HandleWithStateHash> totalParents;
69 std::vector<HandleWithState> next;
70
71 mHandleReconstructions.forEachLiveComponent_const(
72 [&totalParents, &next](bool live, uint64_t componentHandle, uint64_t entityHandle,
73 const HandleWithStateReconstruction& item) {
74 for (int state = BEGIN; state < HANDLE_STATE_COUNT; state++) {
75 const auto& parents = item.states[state].parentHandles;
76 HandleWithState handleWithState = {entityHandle, static_cast<HandleState>(state)};
77 totalParents[handleWithState] = parents.size();
78 if (parents.empty()) {
79 next.push_back(handleWithState);
80 }
81 }
82 });
83
84 std::vector<std::vector<HandleWithState>> handlesByTopoOrder;
85
86 while (!next.empty()) {
87 next = typeTagSortedHandles(next);
88 handlesByTopoOrder.push_back(std::move(next));
89 const std::vector<HandleWithState>& current = handlesByTopoOrder.back();
90 for (const auto& handle : current) {
91 const auto& item = mHandleReconstructions.get(handle.first)->states[handle.second];
92 for (const auto& childHandle : item.childHandles) {
93 if (--totalParents[childHandle] == 0) {
94 next.push_back(childHandle);
95 }
96 }
97 }
98 }
99
100 std::vector<std::vector<uint64_t>> uniqApiRefsByTopoOrder;
101 uniqApiRefsByTopoOrder.reserve(handlesByTopoOrder.size() + 1);
102 for (const auto& handles : handlesByTopoOrder) {
103 std::vector<uint64_t> nextApis;
104 for (const auto& handle : handles) {
105 auto item = mHandleReconstructions.get(handle.first)->states[handle.second];
106 for (uint64_t apiRef : item.apiRefs) {
107 #if DEBUG_RECONSTRUCTION
108 auto apiItem = mApiTrace.get(apiRef);
109 DEBUG_RECON("adding handle 0x%lx API 0x%lx op code %d\n", handle.first, apiRef,
110 apiItem->opCode);
111 #endif
112 nextApis.push_back(apiRef);
113 }
114 }
115 uniqApiRefsByTopoOrder.push_back(std::move(nextApis));
116 }
117
118 uniqApiRefsByTopoOrder.push_back(getOrderedUniqueModifyApis());
119
120 size_t totalApiTraceSize = 0; // 4 bytes to store size of created handles
121
122 for (size_t i = 0; i < uniqApiRefsByTopoOrder.size(); ++i) {
123 for (auto apiHandle : uniqApiRefsByTopoOrder[i]) {
124 auto item = mApiTrace.get(apiHandle);
125 totalApiTraceSize += 4; // opcode
126 totalApiTraceSize += 4; // buffer size of trace
127 totalApiTraceSize += item->traceBytes; // the actual trace
128 }
129 }
130
131 DEBUG_RECON("total api trace size: %zu", totalApiTraceSize);
132
133 std::vector<uint64_t> createdHandleBuffer;
134
135 for (size_t i = 0; i < uniqApiRefsByTopoOrder.size(); ++i) {
136 for (auto apiHandle : uniqApiRefsByTopoOrder[i]) {
137 auto item = mApiTrace.get(apiHandle);
138 for (auto createdHandle : item->createdHandles) {
139 DEBUG_RECON("save handle: 0x%lx\n", createdHandle);
140 createdHandleBuffer.push_back(createdHandle);
141 }
142 }
143 }
144
145 std::vector<uint8_t> apiTraceBuffer;
146 apiTraceBuffer.resize(totalApiTraceSize);
147
148 uint8_t* apiTracePtr = apiTraceBuffer.data();
149
150 for (size_t i = 0; i < uniqApiRefsByTopoOrder.size(); ++i) {
151 for (auto apiHandle : uniqApiRefsByTopoOrder[i]) {
152 auto item = mApiTrace.get(apiHandle);
153 // 4 bytes for opcode, and 4 bytes for saveBufferRaw's size field
154 DEBUG_RECON("saving api handle 0x%lx op code %d\n", apiHandle, item->opCode);
155 memcpy(apiTracePtr, &item->opCode, sizeof(uint32_t));
156 apiTracePtr += 4;
157 uint32_t traceBytesForSnapshot = item->traceBytes + 8;
158 memcpy(apiTracePtr, &traceBytesForSnapshot,
159 sizeof(uint32_t)); // and 8 bytes for 'self' struct of { opcode, packetlen } as
160 // that is what decoder expects
161 apiTracePtr += 4;
162 memcpy(apiTracePtr, item->trace.data(), item->traceBytes);
163 apiTracePtr += item->traceBytes;
164 }
165 }
166
167 DEBUG_RECON("created handle buffer size: %zu trace: %zu", createdHandleBuffer.size(),
168 apiTraceBuffer.size());
169
170 android::base::saveBufferRaw(stream, (char*)(createdHandleBuffer.data()),
171 createdHandleBuffer.size() * sizeof(uint64_t));
172 android::base::saveBufferRaw(stream, (char*)(apiTraceBuffer.data()), apiTraceBuffer.size());
173 }
174
175 class TrivialStream : public IOStream {
176 public:
TrivialStream()177 TrivialStream() : IOStream(4) {}
178 virtual ~TrivialStream() = default;
179
allocBuffer(size_t minSize)180 void* allocBuffer(size_t minSize) {
181 size_t allocSize = (m_bufsize < minSize ? minSize : m_bufsize);
182 if (!m_buf) {
183 m_buf = (unsigned char*)malloc(allocSize);
184 } else if (m_bufsize < allocSize) {
185 unsigned char* p = (unsigned char*)realloc(m_buf, allocSize);
186 if (p != NULL) {
187 m_buf = p;
188 m_bufsize = allocSize;
189 } else {
190 ERR("realloc (%zu) failed\n", allocSize);
191 free(m_buf);
192 m_buf = NULL;
193 m_bufsize = 0;
194 }
195 }
196
197 return m_buf;
198 }
199
commitBuffer(size_t size)200 int commitBuffer(size_t size) {
201 if (size == 0) return 0;
202 return writeFully(m_buf, size);
203 }
204
writeFully(const void * buf,size_t len)205 int writeFully(const void* buf, size_t len) { return 0; }
206
readFully(void * buf,size_t len)207 const unsigned char* readFully(void* buf, size_t len) { return NULL; }
208
getDmaForReading(uint64_t guest_paddr)209 virtual void* getDmaForReading(uint64_t guest_paddr) { return nullptr; }
unlockDma(uint64_t guest_paddr)210 virtual void unlockDma(uint64_t guest_paddr) {}
211
212 protected:
readRaw(void * buf,size_t * inout_len)213 virtual const unsigned char* readRaw(void* buf, size_t* inout_len) { return nullptr; }
onSave(android::base::Stream * stream)214 virtual void onSave(android::base::Stream* stream) {}
onLoad(android::base::Stream * stream)215 virtual unsigned char* onLoad(android::base::Stream* stream) { return nullptr; }
216 };
217
load(android::base::Stream * stream,emugl::GfxApiLogger & gfxLogger,emugl::HealthMonitor<> * healthMonitor)218 void VkReconstruction::load(android::base::Stream* stream, emugl::GfxApiLogger& gfxLogger,
219 emugl::HealthMonitor<>* healthMonitor) {
220 DEBUG_RECON("start. assuming VkDecoderGlobalState has been cleared for loading already");
221 mApiTrace.clear();
222 mHandleReconstructions.clear();
223
224 std::vector<uint8_t> createdHandleBuffer;
225 std::vector<uint8_t> apiTraceBuffer;
226
227 android::base::loadBuffer(stream, &createdHandleBuffer);
228 android::base::loadBuffer(stream, &apiTraceBuffer);
229
230 DEBUG_RECON("created handle buffer size: %zu trace: %zu", createdHandleBuffer.size(),
231 apiTraceBuffer.size());
232
233 uint32_t createdHandleBufferSize = createdHandleBuffer.size();
234
235 mLoadedTrace.resize(4 + createdHandleBufferSize + apiTraceBuffer.size());
236
237 unsigned char* finalTraceData = (unsigned char*)(mLoadedTrace.data());
238
239 memcpy(finalTraceData, &createdHandleBufferSize, sizeof(uint32_t));
240 memcpy(finalTraceData + 4, createdHandleBuffer.data(), createdHandleBufferSize);
241 memcpy(finalTraceData + 4 + createdHandleBufferSize, apiTraceBuffer.data(),
242 apiTraceBuffer.size());
243
244 VkDecoder decoderForLoading;
245 // A decoder that is set for snapshot load will load up the created handles first,
246 // if any, allowing us to 'catch' the results as they are decoded.
247 decoderForLoading.setForSnapshotLoad(true);
248 TrivialStream trivialStream;
249
250 DEBUG_RECON("start decoding trace");
251
252 // TODO: This needs to be the puid seqno ptr
253 auto resources = ProcessResources::create();
254 VkDecoderContext context = {
255 .processName = nullptr,
256 .gfxApiLogger = &gfxLogger,
257 .healthMonitor = healthMonitor,
258 };
259 decoderForLoading.decode(mLoadedTrace.data(), mLoadedTrace.size(), &trivialStream, resources.get(),
260 context);
261
262 DEBUG_RECON("finished decoding trace");
263 }
264
createApiInfo()265 VkReconstruction::ApiHandle VkReconstruction::createApiInfo() {
266 auto handle = mApiTrace.add(ApiInfo(), 1);
267 return handle;
268 }
269
destroyApiInfo(VkReconstruction::ApiHandle h)270 void VkReconstruction::destroyApiInfo(VkReconstruction::ApiHandle h) {
271 auto item = mApiTrace.get(h);
272
273 if (!item) return;
274
275 item->traceBytes = 0;
276 item->createdHandles.clear();
277
278 mApiTrace.remove(h);
279 }
280
getApiInfo(VkReconstruction::ApiHandle h)281 VkReconstruction::ApiInfo* VkReconstruction::getApiInfo(VkReconstruction::ApiHandle h) {
282 return mApiTrace.get(h);
283 }
284
setApiTrace(VkReconstruction::ApiInfo * apiInfo,uint32_t opCode,const uint8_t * traceBegin,size_t traceBytes)285 void VkReconstruction::setApiTrace(VkReconstruction::ApiInfo* apiInfo, uint32_t opCode,
286 const uint8_t* traceBegin, size_t traceBytes) {
287 if (apiInfo->trace.size() < traceBytes) apiInfo->trace.resize(traceBytes);
288 apiInfo->opCode = opCode;
289 memcpy(apiInfo->trace.data(), traceBegin, traceBytes);
290 apiInfo->traceBytes = traceBytes;
291 }
292
dump()293 void VkReconstruction::dump() {
294 fprintf(stderr, "%s: api trace dump\n", __func__);
295
296 size_t traceBytesTotal = 0;
297
298 mApiTrace.forEachLiveEntry_const(
299 [&traceBytesTotal](bool live, uint64_t handle, const ApiInfo& info) {
300 fprintf(stderr, "VkReconstruction::%s: api handle 0x%llx: %s\n", __func__,
301 (unsigned long long)handle, api_opcode_to_string(info.opCode));
302 traceBytesTotal += info.traceBytes;
303 });
304
305 mHandleReconstructions.forEachLiveComponent_const(
306 [this](bool live, uint64_t componentHandle, uint64_t entityHandle,
307 const HandleWithStateReconstruction& reconstruction) {
308 fprintf(stderr, "VkReconstruction::%s: %p handle 0x%llx api refs:\n", __func__, this,
309 (unsigned long long)entityHandle);
310 for (const auto& state : reconstruction.states) {
311 for (auto apiHandle : state.apiRefs) {
312 auto apiInfo = mApiTrace.get(apiHandle);
313 const char* apiName =
314 apiInfo ? api_opcode_to_string(apiInfo->opCode) : "unalloced";
315 fprintf(stderr, "VkReconstruction::%s: 0x%llx: %s\n", __func__,
316 (unsigned long long)apiHandle, apiName);
317 for (auto createdHandle : apiInfo->createdHandles) {
318 fprintf(stderr, "VkReconstruction::%s: created 0x%llx\n", __func__,
319 (unsigned long long)createdHandle);
320 }
321 }
322 }
323 });
324
325 mHandleModifications.forEachLiveComponent_const([this](bool live, uint64_t componentHandle,
326 uint64_t entityHandle,
327 const HandleModification& modification) {
328 fprintf(stderr, "VkReconstruction::%s: mod: %p handle 0x%llx api refs:\n", __func__, this,
329 (unsigned long long)entityHandle);
330 for (auto apiHandle : modification.apiRefs) {
331 auto apiInfo = mApiTrace.get(apiHandle);
332 const char* apiName = apiInfo ? api_opcode_to_string(apiInfo->opCode) : "unalloced";
333 fprintf(stderr, "VkReconstruction::%s: mod: 0x%llx: %s\n", __func__,
334 (unsigned long long)apiHandle, apiName);
335 }
336 });
337
338 fprintf(stderr, "%s: total trace bytes: %zu\n", __func__, traceBytesTotal);
339 }
340
addHandles(const uint64_t * toAdd,uint32_t count)341 void VkReconstruction::addHandles(const uint64_t* toAdd, uint32_t count) {
342 if (!toAdd) return;
343
344 for (uint32_t i = 0; i < count; ++i) {
345 DEBUG_RECON("add 0x%llx", (unsigned long long)toAdd[i]);
346 mHandleReconstructions.add(toAdd[i], HandleWithStateReconstruction());
347 }
348 }
349
removeHandles(const uint64_t * toRemove,uint32_t count,bool recursive)350 void VkReconstruction::removeHandles(const uint64_t* toRemove, uint32_t count, bool recursive) {
351 if (!toRemove) return;
352
353 for (uint32_t i = 0; i < count; ++i) {
354 DEBUG_RECON("remove 0x%llx", (unsigned long long)toRemove[i]);
355 auto item = mHandleReconstructions.get(toRemove[i]);
356 // Delete can happen in arbitrary order.
357 // It might delete the parents before children, which will automatically remove
358 // the name.
359 if (!item) continue;
360 // Break circuler references
361 if (item->destroying) continue;
362 item->destroying = true;
363 if (!recursive) {
364 bool couldDestroy = true;
365 for (const auto& state : item->states) {
366 if (!state.childHandles.size()) {
367 continue;
368 }
369 couldDestroy = false;
370 break;
371 }
372 // TODO(b/330769702): perform delayed destroy when all children are destroyed.
373 if (couldDestroy) {
374 forEachHandleDeleteApi(toRemove + i, 1);
375 mHandleReconstructions.remove(toRemove[i]);
376 } else {
377 DEBUG_RECON("delay destroy of 0x%lx, TODO: actually destroy it", toRemove[i]);
378 item->delayed_destroy = true;
379 item->destroying = false;
380 }
381 continue;
382 }
383 for (size_t j = 0; j < item->states.size(); j++) {
384 for (const auto& parentHandle : item->states[j].parentHandles) {
385 auto parentItem = mHandleReconstructions.get(parentHandle.first);
386 if (!parentItem) {
387 continue;
388 }
389 parentItem->states[parentHandle.second].childHandles.erase(
390 {toRemove[i], static_cast<HandleState>(j)});
391 }
392 item->states[j].parentHandles.clear();
393 std::vector<uint64_t> childHandles;
394 for (const auto& childHandle : item->states[j].childHandles) {
395 if (childHandle.second == CREATED) {
396 childHandles.push_back(childHandle.first);
397 }
398 }
399 item->states[j].childHandles.clear();
400 removeHandles(childHandles.data(), childHandles.size());
401 }
402 forEachHandleDeleteApi(toRemove + i, 1);
403 mHandleReconstructions.remove(toRemove[i]);
404 }
405 }
406
forEachHandleAddApi(const uint64_t * toProcess,uint32_t count,uint64_t apiHandle,HandleState state)407 void VkReconstruction::forEachHandleAddApi(const uint64_t* toProcess, uint32_t count,
408 uint64_t apiHandle, HandleState state) {
409 if (!toProcess) return;
410
411 for (uint32_t i = 0; i < count; ++i) {
412 auto item = mHandleReconstructions.get(toProcess[i]);
413 if (!item) continue;
414
415 item->states[state].apiRefs.push_back(apiHandle);
416 DEBUG_RECON("handle 0x%lx state %d added api 0x%lx", toProcess[i], state, apiHandle);
417 }
418 }
419
forEachHandleDeleteApi(const uint64_t * toProcess,uint32_t count)420 void VkReconstruction::forEachHandleDeleteApi(const uint64_t* toProcess, uint32_t count) {
421 if (!toProcess) return;
422
423 for (uint32_t i = 0; i < count; ++i) {
424 DEBUG_RECON("deleting api for 0x%lx\n", toProcess[i]);
425 auto item = mHandleReconstructions.get(toProcess[i]);
426
427 if (!item) continue;
428
429 for (auto& state : item->states) {
430 for (auto handle : state.apiRefs) {
431 destroyApiInfo(handle);
432 }
433 state.apiRefs.clear();
434 }
435
436 auto modifyItem = mHandleModifications.get(toProcess[i]);
437
438 if (!modifyItem) continue;
439
440 modifyItem->apiRefs.clear();
441 }
442 }
443
addHandleDependency(const uint64_t * handles,uint32_t count,uint64_t parentHandle,HandleState childState,HandleState parentState)444 void VkReconstruction::addHandleDependency(const uint64_t* handles, uint32_t count,
445 uint64_t parentHandle, HandleState childState,
446 HandleState parentState) {
447 if (!handles) return;
448
449 auto parentItem = mHandleReconstructions.get(parentHandle);
450
451 if (!parentItem) {
452 DEBUG_RECON("WARN: adding null parent item: 0x%lx\n", parentHandle);
453 return;
454 }
455 auto& parentItemState = parentItem->states[parentState];
456
457 for (uint32_t i = 0; i < count; ++i) {
458 auto childItem = mHandleReconstructions.get(handles[i]);
459 if (!childItem) {
460 continue;
461 }
462 parentItemState.childHandles.insert({handles[i], static_cast<HandleState>(childState)});
463 childItem->states[childState].parentHandles.push_back(
464 {parentHandle, static_cast<HandleState>(parentState)});
465 DEBUG_RECON("Child handle 0x%lx state %d depends on parent handle 0x%lx state %d",
466 handles[i], childState, parentHandle, parentState);
467 }
468 }
469
setCreatedHandlesForApi(uint64_t apiHandle,const uint64_t * created,uint32_t count)470 void VkReconstruction::setCreatedHandlesForApi(uint64_t apiHandle, const uint64_t* created,
471 uint32_t count) {
472 if (!created) return;
473
474 auto item = mApiTrace.get(apiHandle);
475
476 if (!item) return;
477
478 item->createdHandles.insert(item->createdHandles.end(), created, created + count);
479 item->createdHandles.insert(item->createdHandles.end(), mExtraHandlesForNextApi.begin(),
480 mExtraHandlesForNextApi.end());
481 mExtraHandlesForNextApi.clear();
482 }
483
createExtraHandlesForNextApi(const uint64_t * created,uint32_t count)484 void VkReconstruction::createExtraHandlesForNextApi(const uint64_t* created, uint32_t count) {
485 mExtraHandlesForNextApi.assign(created, created + count);
486 }
487
forEachHandleAddModifyApi(const uint64_t * toProcess,uint32_t count,uint64_t apiHandle)488 void VkReconstruction::forEachHandleAddModifyApi(const uint64_t* toProcess, uint32_t count,
489 uint64_t apiHandle) {
490 if (!toProcess) return;
491
492 for (uint32_t i = 0; i < count; ++i) {
493 mHandleModifications.add(toProcess[i], HandleModification());
494
495 auto item = mHandleModifications.get(toProcess[i]);
496
497 if (!item) continue;
498
499 item->apiRefs.push_back(apiHandle);
500 }
501 }
502
forEachHandleClearModifyApi(const uint64_t * toProcess,uint32_t count)503 void VkReconstruction::forEachHandleClearModifyApi(const uint64_t* toProcess, uint32_t count) {
504 if (!toProcess) return;
505
506 for (uint32_t i = 0; i < count; ++i) {
507 mHandleModifications.add(toProcess[i], HandleModification());
508
509 auto item = mHandleModifications.get(toProcess[i]);
510
511 if (!item) continue;
512
513 item->apiRefs.clear();
514 }
515 }
516
getOrderedUniqueModifyApis() const517 std::vector<uint64_t> VkReconstruction::getOrderedUniqueModifyApis() const {
518 std::vector<HandleModification> orderedModifies;
519
520 // Now add all handle modifications to the trace, ordered by the .order field.
521 mHandleModifications.forEachLiveComponent_const(
522 [&orderedModifies](bool live, uint64_t componentHandle, uint64_t entityHandle,
523 const HandleModification& mod) { orderedModifies.push_back(mod); });
524
525 // Sort by the |order| field for each modify API
526 // since it may be important to apply modifies in a particular
527 // order (e.g., when dealing with descriptor set updates
528 // or commands in a command buffer).
529 std::sort(orderedModifies.begin(), orderedModifies.end(),
530 [](const HandleModification& lhs, const HandleModification& rhs) {
531 return lhs.order < rhs.order;
532 });
533
534 std::unordered_set<uint64_t> usedModifyApis;
535 std::vector<uint64_t> orderedUniqueModifyApis;
536
537 for (const auto& mod : orderedModifies) {
538 for (auto apiRef : mod.apiRefs) {
539 if (usedModifyApis.find(apiRef) == usedModifyApis.end()) {
540 orderedUniqueModifyApis.push_back(apiRef);
541 usedModifyApis.insert(apiRef);
542 }
543 }
544 }
545
546 return orderedUniqueModifyApis;
547 }
548
549 } // namespace vk
550 } // namespace gfxstream
551