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