• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "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<uint64_t> & handles)42 std::vector<uint64_t> typeTagSortedHandles(const std::vector<uint64_t>& handles) {
43     using EntityManagerTypeForHandles = android::base::EntityManager<32, 16, 16, int>;
44 
45     std::vector<uint64_t> res = handles;
46 
47     std::sort(res.begin(), res.end(), [](uint64_t lhs, uint64_t rhs) {
48         return EntityManagerTypeForHandles::getHandleType(lhs) <
49                EntityManagerTypeForHandles::getHandleType(rhs);
50     });
51 
52     return res;
53 }
54 
save(android::base::Stream * stream)55 void VkReconstruction::save(android::base::Stream* stream) {
56     DEBUG_RECON("start")
57 
58 #if DEBUG_RECON
59     dump();
60 #endif
61 
62     std::unordered_map<uint64_t, uint64_t> backDeps;
63 
64     mHandleReconstructions.forEachLiveComponent_const(
65         [&backDeps](bool live, uint64_t componentHandle, uint64_t entityHandle,
66                     const HandleReconstruction& item) {
67             for (auto handle : item.childHandles) {
68                 backDeps[handle] = entityHandle;
69             }
70         });
71 
72     std::vector<uint64_t> topoOrder;
73 
74     mHandleReconstructions.forEachLiveComponent_const(
75         [&topoOrder, &backDeps](bool live, uint64_t componentHandle, uint64_t entityHandle,
76                                 const HandleReconstruction& item) {
77             // Start with populating the roots
78             if (backDeps.find(entityHandle) == backDeps.end()) {
79                 DEBUG_RECON("found root: 0x%llx", (unsigned long long)entityHandle);
80                 topoOrder.push_back(entityHandle);
81             }
82         });
83 
84     std::vector<uint64_t> next;
85 
86     std::unordered_map<uint64_t, uint64_t> uniqApiRefsToTopoOrder;
87     std::unordered_map<uint64_t, std::vector<uint64_t>> uniqApiRefsByTopoOrder;
88     std::unordered_map<uint64_t, std::vector<uint64_t>> uniqApiRefsByTopoAndDependencyOrder;
89 
90     size_t topoLevel = 0;
91 
92     topoOrder = typeTagSortedHandles(topoOrder);
93 
94     while (!topoOrder.empty()) {
95         next.clear();
96 
97         for (auto handle : topoOrder) {
98             auto item = mHandleReconstructions.get(handle);
99 
100             for (auto apiHandle : item->apiRefs) {
101                 if (uniqApiRefsToTopoOrder.find(apiHandle) == uniqApiRefsToTopoOrder.end()) {
102                     DEBUG_RECON("level %zu: 0x%llx api ref: 0x%llx", topoLevel,
103                                 (unsigned long long)handle, (unsigned long long)apiHandle);
104                     auto& refs = uniqApiRefsByTopoOrder[topoLevel];
105                     refs.push_back(apiHandle);
106                 }
107 
108                 uniqApiRefsToTopoOrder[apiHandle] = topoLevel;
109             }
110 
111             for (auto childHandle : item->childHandles) {
112                 next.push_back(childHandle);
113             }
114         }
115 
116         next = typeTagSortedHandles(next);
117 
118         topoOrder = next;
119         ++topoLevel;
120     }
121 
122     uniqApiRefsByTopoOrder[topoLevel] = getOrderedUniqueModifyApis();
123     ++topoLevel;
124 
125     size_t totalApiTraceSize = 0;  // 4 bytes to store size of created handles
126 
127     for (size_t i = 0; i < topoLevel; ++i) {
128         for (auto apiHandle : uniqApiRefsByTopoOrder[i]) {
129             auto item = mApiTrace.get(apiHandle);
130             totalApiTraceSize += 4;                 // opcode
131             totalApiTraceSize += 4;                 // buffer size of trace
132             totalApiTraceSize += item->traceBytes;  // the actual trace
133         }
134     }
135 
136     DEBUG_RECON("total api trace size: %zu", totalApiTraceSize);
137 
138     std::vector<uint64_t> createdHandleBuffer;
139 
140     for (size_t i = 0; i < topoLevel; ++i) {
141         for (auto apiHandle : uniqApiRefsByTopoOrder[i]) {
142             auto item = mApiTrace.get(apiHandle);
143             for (auto createdHandle : item->createdHandles) {
144                 DEBUG_RECON("save handle: 0x%llx\n", createdHandle);
145                 createdHandleBuffer.push_back(createdHandle);
146             }
147         }
148     }
149 
150     std::vector<uint8_t> apiTraceBuffer;
151     apiTraceBuffer.resize(totalApiTraceSize);
152 
153     uint8_t* apiTracePtr = apiTraceBuffer.data();
154 
155     for (size_t i = 0; i < topoLevel; ++i) {
156         for (auto apiHandle : uniqApiRefsByTopoOrder[i]) {
157             auto item = mApiTrace.get(apiHandle);
158             // 4 bytes for opcode, and 4 bytes for saveBufferRaw's size field
159             memcpy(apiTracePtr, &item->opCode, sizeof(uint32_t));
160             apiTracePtr += 4;
161             uint32_t traceBytesForSnapshot = item->traceBytes + 8;
162             memcpy(apiTracePtr, &traceBytesForSnapshot,
163                    sizeof(uint32_t));  // and 8 bytes for 'self' struct of { opcode, packetlen } as
164                                        // that is what decoder expects
165             apiTracePtr += 4;
166             memcpy(apiTracePtr, item->trace.data(), item->traceBytes);
167             apiTracePtr += item->traceBytes;
168         }
169     }
170 
171     DEBUG_RECON("created handle buffer size: %zu trace: %zu", createdHandleBuffer.size(),
172                 apiTraceBuffer.size());
173 
174     android::base::saveBufferRaw(stream, (char*)(createdHandleBuffer.data()),
175                                  createdHandleBuffer.size() * sizeof(uint64_t));
176     android::base::saveBufferRaw(stream, (char*)(apiTraceBuffer.data()), apiTraceBuffer.size());
177 }
178 
179 class TrivialStream : public IOStream {
180    public:
TrivialStream()181     TrivialStream() : IOStream(4) {}
182     virtual ~TrivialStream() = default;
183 
allocBuffer(size_t minSize)184     void* allocBuffer(size_t minSize) {
185         size_t allocSize = (m_bufsize < minSize ? minSize : m_bufsize);
186         if (!m_buf) {
187             m_buf = (unsigned char*)malloc(allocSize);
188         } else if (m_bufsize < allocSize) {
189             unsigned char* p = (unsigned char*)realloc(m_buf, allocSize);
190             if (p != NULL) {
191                 m_buf = p;
192                 m_bufsize = allocSize;
193             } else {
194                 ERR("realloc (%zu) failed\n", allocSize);
195                 free(m_buf);
196                 m_buf = NULL;
197                 m_bufsize = 0;
198             }
199         }
200 
201         return m_buf;
202     }
203 
commitBuffer(size_t size)204     int commitBuffer(size_t size) {
205         if (size == 0) return 0;
206         return writeFully(m_buf, size);
207     }
208 
writeFully(const void * buf,size_t len)209     int writeFully(const void* buf, size_t len) { return 0; }
210 
readFully(void * buf,size_t len)211     const unsigned char* readFully(void* buf, size_t len) { return NULL; }
212 
getDmaForReading(uint64_t guest_paddr)213     virtual void* getDmaForReading(uint64_t guest_paddr) { return nullptr; }
unlockDma(uint64_t guest_paddr)214     virtual void unlockDma(uint64_t guest_paddr) {}
215 
216    protected:
readRaw(void * buf,size_t * inout_len)217     virtual const unsigned char* readRaw(void* buf, size_t* inout_len) { return nullptr; }
onSave(android::base::Stream * stream)218     virtual void onSave(android::base::Stream* stream) {}
onLoad(android::base::Stream * stream)219     virtual unsigned char* onLoad(android::base::Stream* stream) { return nullptr; }
220 };
221 
load(android::base::Stream * stream,emugl::GfxApiLogger & gfxLogger,emugl::HealthMonitor<> * healthMonitor)222 void VkReconstruction::load(android::base::Stream* stream, emugl::GfxApiLogger& gfxLogger,
223                             emugl::HealthMonitor<>* healthMonitor) {
224     DEBUG_RECON("start. assuming VkDecoderGlobalState has been cleared for loading already");
225     mApiTrace.clear();
226     mHandleReconstructions.clear();
227 
228     std::vector<uint8_t> createdHandleBuffer;
229     std::vector<uint8_t> apiTraceBuffer;
230 
231     android::base::loadBuffer(stream, &createdHandleBuffer);
232     android::base::loadBuffer(stream, &apiTraceBuffer);
233 
234     DEBUG_RECON("created handle buffer size: %zu trace: %zu", createdHandleBuffer.size(),
235                 apiTraceBuffer.size());
236 
237     uint32_t createdHandleBufferSize = createdHandleBuffer.size();
238 
239     mLoadedTrace.resize(4 + createdHandleBufferSize + apiTraceBuffer.size());
240 
241     unsigned char* finalTraceData = (unsigned char*)(mLoadedTrace.data());
242 
243     memcpy(finalTraceData, &createdHandleBufferSize, sizeof(uint32_t));
244     memcpy(finalTraceData + 4, createdHandleBuffer.data(), createdHandleBufferSize);
245     memcpy(finalTraceData + 4 + createdHandleBufferSize, apiTraceBuffer.data(),
246            apiTraceBuffer.size());
247 
248     VkDecoder decoderForLoading;
249     // A decoder that is set for snapshot load will load up the created handles first,
250     // if any, allowing us to 'catch' the results as they are decoded.
251     decoderForLoading.setForSnapshotLoad(true);
252     TrivialStream trivialStream;
253 
254     DEBUG_RECON("start decoding trace");
255 
256     // TODO: This needs to be the puid seqno ptr
257     auto resources = ProcessResources::create();
258     VkDecoderContext context = {
259         .processName = nullptr,
260         .gfxApiLogger = &gfxLogger,
261         .healthMonitor = healthMonitor,
262     };
263     decoderForLoading.decode(mLoadedTrace.data(), mLoadedTrace.size(), &trivialStream, resources.get(),
264                              context);
265 
266     DEBUG_RECON("finished decoding trace");
267 }
268 
createApiInfo()269 VkReconstruction::ApiHandle VkReconstruction::createApiInfo() {
270     auto handle = mApiTrace.add(ApiInfo(), 1);
271     return handle;
272 }
273 
destroyApiInfo(VkReconstruction::ApiHandle h)274 void VkReconstruction::destroyApiInfo(VkReconstruction::ApiHandle h) {
275     auto item = mApiTrace.get(h);
276 
277     if (!item) return;
278 
279     item->traceBytes = 0;
280     item->createdHandles.clear();
281 
282     mApiTrace.remove(h);
283 }
284 
getApiInfo(VkReconstruction::ApiHandle h)285 VkReconstruction::ApiInfo* VkReconstruction::getApiInfo(VkReconstruction::ApiHandle h) {
286     return mApiTrace.get(h);
287 }
288 
setApiTrace(VkReconstruction::ApiInfo * apiInfo,uint32_t opCode,const uint8_t * traceBegin,size_t traceBytes)289 void VkReconstruction::setApiTrace(VkReconstruction::ApiInfo* apiInfo, uint32_t opCode,
290                                    const uint8_t* traceBegin, size_t traceBytes) {
291     if (apiInfo->trace.size() < traceBytes) apiInfo->trace.resize(traceBytes);
292     apiInfo->opCode = opCode;
293     memcpy(apiInfo->trace.data(), traceBegin, traceBytes);
294     apiInfo->traceBytes = traceBytes;
295 }
296 
dump()297 void VkReconstruction::dump() {
298     fprintf(stderr, "%s: api trace dump\n", __func__);
299 
300     size_t traceBytesTotal = 0;
301 
302     mApiTrace.forEachLiveEntry_const(
303         [&traceBytesTotal](bool live, uint64_t handle, const ApiInfo& info) {
304             fprintf(stderr, "VkReconstruction::%s: api handle 0x%llx: %s\n", __func__,
305                     (unsigned long long)handle, api_opcode_to_string(info.opCode));
306             traceBytesTotal += info.traceBytes;
307         });
308 
309     mHandleReconstructions.forEachLiveComponent_const(
310         [this](bool live, uint64_t componentHandle, uint64_t entityHandle,
311                const HandleReconstruction& reconstruction) {
312             fprintf(stderr, "VkReconstruction::%s: %p handle 0x%llx api refs:\n", __func__, this,
313                     (unsigned long long)entityHandle);
314             for (auto apiHandle : reconstruction.apiRefs) {
315                 auto apiInfo = mApiTrace.get(apiHandle);
316                 const char* apiName = apiInfo ? api_opcode_to_string(apiInfo->opCode) : "unalloced";
317                 fprintf(stderr, "VkReconstruction::%s:     0x%llx: %s\n", __func__,
318                         (unsigned long long)apiHandle, apiName);
319                 for (auto createdHandle : apiInfo->createdHandles) {
320                     fprintf(stderr, "VkReconstruction::%s:         created 0x%llx\n", __func__,
321                             (unsigned long long)createdHandle);
322                 }
323             }
324         });
325 
326     mHandleModifications.forEachLiveComponent_const([this](bool live, uint64_t componentHandle,
327                                                            uint64_t entityHandle,
328                                                            const HandleModification& modification) {
329         fprintf(stderr, "VkReconstruction::%s: mod: %p handle 0x%llx api refs:\n", __func__, this,
330                 (unsigned long long)entityHandle);
331         for (auto apiHandle : modification.apiRefs) {
332             auto apiInfo = mApiTrace.get(apiHandle);
333             const char* apiName = apiInfo ? api_opcode_to_string(apiInfo->opCode) : "unalloced";
334             fprintf(stderr, "VkReconstruction::%s: mod:     0x%llx: %s\n", __func__,
335                     (unsigned long long)apiHandle, apiName);
336         }
337     });
338 
339     fprintf(stderr, "%s: total trace bytes: %zu\n", __func__, traceBytesTotal);
340 }
341 
addHandles(const uint64_t * toAdd,uint32_t count)342 void VkReconstruction::addHandles(const uint64_t* toAdd, uint32_t count) {
343     if (!toAdd) return;
344 
345     for (uint32_t i = 0; i < count; ++i) {
346         DEBUG_RECON("add 0x%llx", (unsigned long long)toAdd[i]);
347         mHandleReconstructions.add(toAdd[i], HandleReconstruction());
348     }
349 }
350 
removeHandles(const uint64_t * toRemove,uint32_t count)351 void VkReconstruction::removeHandles(const uint64_t* toRemove, uint32_t count) {
352     if (!toRemove) return;
353 
354     forEachHandleDeleteApi(toRemove, count);
355 
356     for (uint32_t i = 0; i < count; ++i) {
357         DEBUG_RECON("remove 0x%llx", (unsigned long long)toRemove[i]);
358         auto item = mHandleReconstructions.get(toRemove[i]);
359 
360         if (!item) continue;
361 
362         mHandleReconstructions.remove(toRemove[i]);
363 
364         removeHandles(item->childHandles.data(), item->childHandles.size());
365 
366         item->childHandles.clear();
367     }
368 }
369 
forEachHandleAddApi(const uint64_t * toProcess,uint32_t count,uint64_t apiHandle)370 void VkReconstruction::forEachHandleAddApi(const uint64_t* toProcess, uint32_t count,
371                                            uint64_t apiHandle) {
372     if (!toProcess) return;
373 
374     for (uint32_t i = 0; i < count; ++i) {
375         auto item = mHandleReconstructions.get(toProcess[i]);
376 
377         if (!item) continue;
378 
379         item->apiRefs.push_back(apiHandle);
380     }
381 }
382 
forEachHandleDeleteApi(const uint64_t * toProcess,uint32_t count)383 void VkReconstruction::forEachHandleDeleteApi(const uint64_t* toProcess, uint32_t count) {
384     if (!toProcess) return;
385 
386     for (uint32_t i = 0; i < count; ++i) {
387         auto item = mHandleReconstructions.get(toProcess[i]);
388 
389         if (!item) continue;
390 
391         for (auto handle : item->apiRefs) {
392             destroyApiInfo(handle);
393         }
394 
395         item->apiRefs.clear();
396 
397         auto modifyItem = mHandleModifications.get(toProcess[i]);
398 
399         if (!modifyItem) continue;
400 
401         modifyItem->apiRefs.clear();
402     }
403 }
404 
addHandleDependency(const uint64_t * handles,uint32_t count,uint64_t parentHandle)405 void VkReconstruction::addHandleDependency(const uint64_t* handles, uint32_t count,
406                                            uint64_t parentHandle) {
407     if (!handles) return;
408 
409     auto item = mHandleReconstructions.get(parentHandle);
410 
411     if (!item) return;
412 
413     for (uint32_t i = 0; i < count; ++i) {
414         item->childHandles.push_back(handles[i]);
415     }
416 }
417 
setCreatedHandlesForApi(uint64_t apiHandle,const uint64_t * created,uint32_t count)418 void VkReconstruction::setCreatedHandlesForApi(uint64_t apiHandle, const uint64_t* created,
419                                                uint32_t count) {
420     if (!created) return;
421 
422     auto item = mApiTrace.get(apiHandle);
423 
424     if (!item) return;
425 
426     for (uint32_t i = 0; i < count; ++i) {
427         item->createdHandles.push_back(created[i]);
428     }
429 }
430 
forEachHandleAddModifyApi(const uint64_t * toProcess,uint32_t count,uint64_t apiHandle)431 void VkReconstruction::forEachHandleAddModifyApi(const uint64_t* toProcess, uint32_t count,
432                                                  uint64_t apiHandle) {
433     if (!toProcess) return;
434 
435     for (uint32_t i = 0; i < count; ++i) {
436         mHandleModifications.add(toProcess[i], HandleModification());
437 
438         auto item = mHandleModifications.get(toProcess[i]);
439 
440         if (!item) continue;
441 
442         item->apiRefs.push_back(apiHandle);
443     }
444 }
445 
getOrderedUniqueModifyApis() const446 std::vector<uint64_t> VkReconstruction::getOrderedUniqueModifyApis() const {
447     std::vector<HandleModification> orderedModifies;
448 
449     // Now add all handle modifications to the trace, ordered by the .order field.
450     mHandleModifications.forEachLiveComponent_const(
451         [&orderedModifies](bool live, uint64_t componentHandle, uint64_t entityHandle,
452                            const HandleModification& mod) { orderedModifies.push_back(mod); });
453 
454     // Sort by the |order| field for each modify API
455     // since it may be important to apply modifies in a particular
456     // order (e.g., when dealing with descriptor set updates
457     // or commands in a command buffer).
458     std::sort(orderedModifies.begin(), orderedModifies.end(),
459               [](const HandleModification& lhs, const HandleModification& rhs) {
460                   return lhs.order < rhs.order;
461               });
462 
463     std::unordered_set<uint64_t> usedModifyApis;
464     std::vector<uint64_t> orderedUniqueModifyApis;
465 
466     for (const auto& mod : orderedModifies) {
467         for (auto apiRef : mod.apiRefs) {
468             if (usedModifyApis.find(apiRef) == usedModifyApis.end()) {
469                 orderedUniqueModifyApis.push_back(apiRef);
470                 usedModifyApis.insert(apiRef);
471             }
472         }
473     }
474 
475     return orderedUniqueModifyApis;
476 }
477 
478 }  // namespace vk
479 }  // namespace gfxstream
480