• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright 2021 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // frame_capture_test_utils:
7 //   Helper functions for capture and replay of traces.
8 //
9 
10 #include "frame_capture_test_utils.h"
11 
12 #include "common/frame_capture_utils.h"
13 #include "common/string_utils.h"
14 
15 #include <rapidjson/document.h>
16 #include <rapidjson/istreamwrapper.h>
17 #include <fstream>
18 
19 namespace angle
20 {
21 
22 namespace
23 {
LoadJSONFromFile(const std::string & fileName,rapidjson::Document * doc)24 bool LoadJSONFromFile(const std::string &fileName, rapidjson::Document *doc)
25 {
26     std::ifstream ifs(fileName);
27     if (!ifs.is_open())
28     {
29         return false;
30     }
31 
32     rapidjson::IStreamWrapper inWrapper(ifs);
33     doc->ParseStream(inWrapper);
34     return !doc->HasParseError();
35 }
36 
37 // Branched from:
38 // https://crsrc.org/c/third_party/zlib/google/compression_utils_portable.cc;drc=9fc44ce454cc889b603900ccd14b7024ea2c284c;l=167
39 // Unmodified other than inlining ZlibStreamWrapperType and z_stream arg to access .msg
GzipUncompressHelperPatched(Bytef * dest,uLongf * dest_length,const Bytef * source,uLong source_length,z_stream & stream)40 int GzipUncompressHelperPatched(Bytef *dest,
41                                 uLongf *dest_length,
42                                 const Bytef *source,
43                                 uLong source_length,
44                                 z_stream &stream)
45 {
46     stream.next_in  = static_cast<z_const Bytef *>(const_cast<Bytef *>(source));
47     stream.avail_in = static_cast<uInt>(source_length);
48     if (static_cast<uLong>(stream.avail_in) != source_length)
49         return Z_BUF_ERROR;
50 
51     stream.next_out  = dest;
52     stream.avail_out = static_cast<uInt>(*dest_length);
53     if (static_cast<uLong>(stream.avail_out) != *dest_length)
54         return Z_BUF_ERROR;
55 
56     stream.zalloc = static_cast<alloc_func>(0);
57     stream.zfree  = static_cast<free_func>(0);
58 
59     int err = inflateInit2(&stream, MAX_WBITS + 16);
60     if (err != Z_OK)
61         return err;
62 
63     err = inflate(&stream, Z_FINISH);
64     if (err != Z_STREAM_END)
65     {
66         inflateEnd(&stream);
67         if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0))
68             return Z_DATA_ERROR;
69         return err;
70     }
71     *dest_length = stream.total_out;
72 
73     err = inflateEnd(&stream);
74     return err;
75 }
76 
UncompressData(const std::vector<uint8_t> & compressedData,std::vector<uint8_t> * uncompressedData)77 bool UncompressData(const std::vector<uint8_t> &compressedData,
78                     std::vector<uint8_t> *uncompressedData)
79 {
80     uint32_t uncompressedSize =
81         zlib_internal::GetGzipUncompressedSize(compressedData.data(), compressedData.size());
82 
83     uncompressedData->resize(uncompressedSize + 1);  // +1 to make sure .data() is valid
84     uLong destLen = uncompressedSize;
85     z_stream stream;
86     int zResult =
87         GzipUncompressHelperPatched(uncompressedData->data(), &destLen, compressedData.data(),
88                                     static_cast<uLong>(compressedData.size()), stream);
89 
90     if (zResult != Z_OK)
91     {
92         std::cerr << "Failure to decompressed binary data: " << zResult
93                   << " msg=" << (stream.msg ? stream.msg : "nil") << "\n";
94         fprintf(stderr,
95                 "next_in %p (input %p) avail_in %d total_in %lu next_out %p (output %p) avail_out "
96                 "%d total_out %ld adler %lX crc %lX crc_simd %lX\n",
97                 stream.next_in, compressedData.data(), stream.avail_in, stream.total_in,
98                 stream.next_out, uncompressedData->data(), stream.avail_out, stream.total_out,
99                 stream.adler, crc32(0, uncompressedData->data(), uncompressedSize),
100                 crc32(0, uncompressedData->data(), 16 * (uncompressedSize / 16)));
101         return false;
102     }
103 
104     return true;
105 }
106 
SaveDebugFile(const std::string & outputDir,const char * baseFileName,const char * suffix,const std::vector<uint8_t> data)107 void SaveDebugFile(const std::string &outputDir,
108                    const char *baseFileName,
109                    const char *suffix,
110                    const std::vector<uint8_t> data)
111 {
112     if (outputDir.empty())
113     {
114         return;
115     }
116 
117     std::ostringstream path;
118     path << outputDir << "/" << baseFileName << suffix;
119     FILE *fp = fopen(path.str().c_str(), "wb");
120     fwrite(data.data(), 1, data.size(), fp);
121     fclose(fp);
122 }
123 }  // namespace
124 
LoadTraceNamesFromJSON(const std::string jsonFilePath,std::vector<std::string> * namesOut)125 bool LoadTraceNamesFromJSON(const std::string jsonFilePath, std::vector<std::string> *namesOut)
126 {
127     rapidjson::Document doc;
128     if (!LoadJSONFromFile(jsonFilePath, &doc))
129     {
130         return false;
131     }
132 
133     if (!doc.IsArray())
134     {
135         return false;
136     }
137 
138     // Read trace json into a list of trace names.
139     std::vector<std::string> traces;
140 
141     rapidjson::Document::Array traceArray = doc.GetArray();
142     for (rapidjson::SizeType arrayIndex = 0; arrayIndex < traceArray.Size(); ++arrayIndex)
143     {
144         const rapidjson::Document::ValueType &arrayElement = traceArray[arrayIndex];
145 
146         if (!arrayElement.IsString())
147         {
148             return false;
149         }
150 
151         traces.push_back(arrayElement.GetString());
152     }
153 
154     *namesOut = std::move(traces);
155     return true;
156 }
157 
LoadTraceInfoFromJSON(const std::string & traceName,const std::string & traceJsonPath,TraceInfo * traceInfoOut)158 bool LoadTraceInfoFromJSON(const std::string &traceName,
159                            const std::string &traceJsonPath,
160                            TraceInfo *traceInfoOut)
161 {
162     rapidjson::Document doc;
163     if (!LoadJSONFromFile(traceJsonPath, &doc))
164     {
165         return false;
166     }
167 
168     if (!doc.IsObject() || !doc.HasMember("TraceMetadata"))
169     {
170         return false;
171     }
172 
173     const rapidjson::Document::Object &meta = doc["TraceMetadata"].GetObj();
174 
175     strncpy(traceInfoOut->name, traceName.c_str(), kTraceInfoMaxNameLen);
176     traceInfoOut->frameEnd               = meta["FrameEnd"].GetInt();
177     traceInfoOut->frameStart             = meta["FrameStart"].GetInt();
178     traceInfoOut->isBinaryDataCompressed = meta["IsBinaryDataCompressed"].GetBool();
179     traceInfoOut->isCL                   = meta.HasMember("IsOpenCL");
180 
181     if (meta.HasMember("ContextClientMajorVersion"))
182     {
183         traceInfoOut->contextClientMajorVersion = meta["ContextClientMajorVersion"].GetInt();
184         traceInfoOut->contextClientMinorVersion = meta["ContextClientMinorVersion"].GetInt();
185         traceInfoOut->drawSurfaceHeight         = meta["DrawSurfaceHeight"].GetInt();
186         traceInfoOut->drawSurfaceWidth          = meta["DrawSurfaceWidth"].GetInt();
187 
188         angle::HexStringToUInt(meta["DrawSurfaceColorSpace"].GetString(),
189                                &traceInfoOut->drawSurfaceColorSpace);
190         angle::HexStringToUInt(meta["DisplayPlatformType"].GetString(),
191                                &traceInfoOut->displayPlatformType);
192         angle::HexStringToUInt(meta["DisplayDeviceType"].GetString(),
193                                &traceInfoOut->displayDeviceType);
194 
195         traceInfoOut->configRedBits          = meta["ConfigRedBits"].GetInt();
196         traceInfoOut->configGreenBits        = meta["ConfigGreenBits"].GetInt();
197         traceInfoOut->configBlueBits         = meta["ConfigBlueBits"].GetInt();
198         traceInfoOut->configAlphaBits        = meta["ConfigAlphaBits"].GetInt();
199         traceInfoOut->configDepthBits        = meta["ConfigDepthBits"].GetInt();
200         traceInfoOut->configStencilBits      = meta["ConfigStencilBits"].GetInt();
201         traceInfoOut->areClientArraysEnabled = meta["AreClientArraysEnabled"].GetBool();
202         traceInfoOut->isBindGeneratesResourcesEnabled =
203             meta["IsBindGeneratesResourcesEnabled"].GetBool();
204         traceInfoOut->isWebGLCompatibilityEnabled = meta["IsWebGLCompatibilityEnabled"].GetBool();
205         traceInfoOut->isRobustResourceInitEnabled = meta["IsRobustResourceInitEnabled"].GetBool();
206     }
207     else
208     {
209         traceInfoOut->contextClientMajorVersion = 1;
210         traceInfoOut->contextClientMinorVersion = 1;
211         traceInfoOut->drawSurfaceHeight         = 1;
212         traceInfoOut->drawSurfaceWidth          = 1;
213     }
214 
215     if (doc.HasMember("WindowSurfaceContextID"))
216     {
217         traceInfoOut->windowSurfaceContextId = doc["WindowSurfaceContextID"].GetInt();
218     }
219 
220     if (doc.HasMember("RequiredExtensions"))
221     {
222         const rapidjson::Value &requiredExtensions = doc["RequiredExtensions"];
223         if (!requiredExtensions.IsArray())
224         {
225             return false;
226         }
227         for (rapidjson::SizeType i = 0; i < requiredExtensions.Size(); i++)
228         {
229             std::string ext = std::string(requiredExtensions[i].GetString());
230             traceInfoOut->requiredExtensions.push_back(ext);
231         }
232     }
233 
234     if (meta.HasMember("KeyFrames"))
235     {
236         const rapidjson::Value &keyFrames = meta["KeyFrames"];
237         if (!keyFrames.IsArray())
238         {
239             return false;
240         }
241         for (rapidjson::SizeType i = 0; i < keyFrames.Size(); i++)
242         {
243             int frame = keyFrames[i].GetInt();
244             traceInfoOut->keyFrames.push_back(frame);
245         }
246     }
247 
248     const rapidjson::Document::Array &traceFiles = doc["TraceFiles"].GetArray();
249     for (const rapidjson::Value &value : traceFiles)
250     {
251         traceInfoOut->traceFiles.push_back(value.GetString());
252     }
253 
254     traceInfoOut->initialized = true;
255     return true;
256 }
257 
TraceLibrary(const std::string & traceName,const TraceInfo & traceInfo,const std::string & baseDir)258 TraceLibrary::TraceLibrary(const std::string &traceName,
259                            const TraceInfo &traceInfo,
260                            const std::string &baseDir)
261 {
262     std::stringstream libNameStr;
263     SearchType searchType = SearchType::ModuleDir;
264 
265 #if defined(ANGLE_TRACE_EXTERNAL_BINARIES)
266     // This means we are using the binary build of traces on Android, which are
267     // not bundled in the APK, but located in the app's home directory.
268     searchType = SearchType::SystemDir;
269     libNameStr << baseDir;
270 #endif  // defined(ANGLE_TRACE_EXTERNAL_BINARIES)
271 #if !defined(ANGLE_PLATFORM_WINDOWS)
272     libNameStr << "lib";
273 #endif  // !defined(ANGLE_PLATFORM_WINDOWS)
274     libNameStr << traceName;
275     std::string libName = libNameStr.str();
276     std::string loadError;
277 
278     mTraceLibrary.reset(OpenSharedLibraryAndGetError(libName.c_str(), searchType, &loadError));
279     if (mTraceLibrary->getNative() == nullptr)
280     {
281         FATAL() << "Failed to load trace library (" << libName << "): " << loadError;
282     }
283 
284     callFunc<SetupEntryPoints>("SetupEntryPoints", static_cast<angle::TraceCallbacks *>(this),
285                                &mTraceFunctions);
286     mTraceFunctions->SetTraceInfo(traceInfo);
287     mTraceInfo = traceInfo;
288 }
289 
LoadBinaryData(const char * fileName)290 uint8_t *TraceLibrary::LoadBinaryData(const char *fileName)
291 {
292     std::ostringstream pathBuffer;
293     pathBuffer << mBinaryDataDir << "/" << fileName;
294     FILE *fp = fopen(pathBuffer.str().c_str(), "rb");
295     if (fp == 0)
296     {
297         fprintf(stderr, "Error loading binary data file: %s\n", fileName);
298         exit(1);
299     }
300     fseek(fp, 0, SEEK_END);
301     long size = ftell(fp);
302     fseek(fp, 0, SEEK_SET);
303     if (mTraceInfo.isBinaryDataCompressed)
304     {
305         if (!strstr(fileName, ".gz"))
306         {
307             fprintf(stderr, "Filename does not end in .gz");
308             exit(1);
309         }
310 
311         std::vector<uint8_t> compressedData(size);
312         size_t bytesRead = fread(compressedData.data(), 1, size, fp);
313         if (bytesRead != static_cast<size_t>(size))
314         {
315             std::cerr << "Failed to read binary data: " << bytesRead << " != " << size << "\n";
316             exit(1);
317         }
318 
319         if (!UncompressData(compressedData, &mBinaryData))
320         {
321             // Workaround for sporadic failures https://issuetracker.google.com/296921272
322             SaveDebugFile(mDebugOutputDir, fileName, ".gzdbg_input.gz", compressedData);
323             SaveDebugFile(mDebugOutputDir, fileName, ".gzdbg_attempt1", mBinaryData);
324             std::vector<uint8_t> uncompressedData;
325             bool secondResult = UncompressData(compressedData, &uncompressedData);
326             SaveDebugFile(mDebugOutputDir, fileName, ".gzdbg_attempt2", uncompressedData);
327             if (!secondResult)
328             {
329                 std::cerr << "Uncompress retry failed\n";
330                 exit(1);
331             }
332             std::cerr << "Uncompress retry succeeded, moving to mBinaryData\n";
333             mBinaryData = std::move(uncompressedData);
334         }
335     }
336     else
337     {
338         if (!strstr(fileName, ".angledata"))
339         {
340             fprintf(stderr, "Filename does not end in .angledata");
341             exit(1);
342         }
343         mBinaryData.resize(size + 1);
344         (void)fread(mBinaryData.data(), 1, size, fp);
345     }
346     fclose(fp);
347 
348     return mBinaryData.data();
349 }
350 
351 }  // namespace angle
352