• 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.IsObject() || !doc.HasMember("traces") || !doc["traces"].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["traces"].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         std::vector<std::string> traceAndVersion;
152         angle::SplitStringAlongWhitespace(arrayElement.GetString(), &traceAndVersion);
153         traces.push_back(traceAndVersion[0]);
154     }
155 
156     *namesOut = std::move(traces);
157     return true;
158 }
159 
LoadTraceInfoFromJSON(const std::string & traceName,const std::string & traceJsonPath,TraceInfo * traceInfoOut)160 bool LoadTraceInfoFromJSON(const std::string &traceName,
161                            const std::string &traceJsonPath,
162                            TraceInfo *traceInfoOut)
163 {
164     rapidjson::Document doc;
165     if (!LoadJSONFromFile(traceJsonPath, &doc))
166     {
167         return false;
168     }
169 
170     if (!doc.IsObject() || !doc.HasMember("TraceMetadata"))
171     {
172         return false;
173     }
174 
175     const rapidjson::Document::Object &meta = doc["TraceMetadata"].GetObj();
176 
177     strncpy(traceInfoOut->name, traceName.c_str(), kTraceInfoMaxNameLen);
178     traceInfoOut->contextClientMajorVersion = meta["ContextClientMajorVersion"].GetInt();
179     traceInfoOut->contextClientMinorVersion = meta["ContextClientMinorVersion"].GetInt();
180     traceInfoOut->frameEnd                  = meta["FrameEnd"].GetInt();
181     traceInfoOut->frameStart                = meta["FrameStart"].GetInt();
182     traceInfoOut->drawSurfaceHeight         = meta["DrawSurfaceHeight"].GetInt();
183     traceInfoOut->drawSurfaceWidth          = meta["DrawSurfaceWidth"].GetInt();
184 
185     angle::HexStringToUInt(meta["DrawSurfaceColorSpace"].GetString(),
186                            &traceInfoOut->drawSurfaceColorSpace);
187     angle::HexStringToUInt(meta["DisplayPlatformType"].GetString(),
188                            &traceInfoOut->displayPlatformType);
189     angle::HexStringToUInt(meta["DisplayDeviceType"].GetString(), &traceInfoOut->displayDeviceType);
190 
191     traceInfoOut->configRedBits     = meta["ConfigRedBits"].GetInt();
192     traceInfoOut->configGreenBits   = meta["ConfigGreenBits"].GetInt();
193     traceInfoOut->configBlueBits    = meta["ConfigBlueBits"].GetInt();
194     traceInfoOut->configAlphaBits   = meta["ConfigAlphaBits"].GetInt();
195     traceInfoOut->configDepthBits   = meta["ConfigDepthBits"].GetInt();
196     traceInfoOut->configStencilBits = meta["ConfigStencilBits"].GetInt();
197 
198     traceInfoOut->isBinaryDataCompressed = meta["IsBinaryDataCompressed"].GetBool();
199     traceInfoOut->areClientArraysEnabled = meta["AreClientArraysEnabled"].GetBool();
200     traceInfoOut->isBindGeneratesResourcesEnabled =
201         meta["IsBindGeneratesResourcesEnabled"].GetBool();
202     traceInfoOut->isWebGLCompatibilityEnabled = meta["IsWebGLCompatibilityEnabled"].GetBool();
203     traceInfoOut->isRobustResourceInitEnabled = meta["IsRobustResourceInitEnabled"].GetBool();
204     traceInfoOut->windowSurfaceContextId      = doc["WindowSurfaceContextID"].GetInt();
205 
206     if (doc.HasMember("RequiredExtensions"))
207     {
208         const rapidjson::Value &requiredExtensions = doc["RequiredExtensions"];
209         if (!requiredExtensions.IsArray())
210         {
211             return false;
212         }
213         for (rapidjson::SizeType i = 0; i < requiredExtensions.Size(); i++)
214         {
215             std::string ext = std::string(requiredExtensions[i].GetString());
216             traceInfoOut->requiredExtensions.push_back(ext);
217         }
218     }
219 
220     if (meta.HasMember("KeyFrames"))
221     {
222         const rapidjson::Value &keyFrames = meta["KeyFrames"];
223         if (!keyFrames.IsArray())
224         {
225             return false;
226         }
227         for (rapidjson::SizeType i = 0; i < keyFrames.Size(); i++)
228         {
229             int frame = keyFrames[i].GetInt();
230             traceInfoOut->keyFrames.push_back(frame);
231         }
232     }
233 
234     const rapidjson::Document::Array &traceFiles = doc["TraceFiles"].GetArray();
235     for (const rapidjson::Value &value : traceFiles)
236     {
237         traceInfoOut->traceFiles.push_back(value.GetString());
238     }
239 
240     traceInfoOut->initialized = true;
241     return true;
242 }
243 
TraceLibrary(const std::string & traceName,const TraceInfo & traceInfo)244 TraceLibrary::TraceLibrary(const std::string &traceName, const TraceInfo &traceInfo)
245 {
246     std::stringstream libNameStr;
247     SearchType searchType = SearchType::ModuleDir;
248 
249 #if defined(ANGLE_TRACE_EXTERNAL_BINARIES)
250     // This means we are using the binary build of traces on Android, which are
251     // not bundled in the APK, but located in the app's home directory.
252     searchType = SearchType::SystemDir;
253     libNameStr << "/data/user/0/com.android.angle.test/angle_traces/";
254 #endif  // defined(ANGLE_TRACE_EXTERNAL_BINARIES)
255 #if !defined(ANGLE_PLATFORM_WINDOWS)
256     libNameStr << "lib";
257 #endif  // !defined(ANGLE_PLATFORM_WINDOWS)
258     libNameStr << traceName;
259     std::string libName = libNameStr.str();
260     std::string loadError;
261     mTraceLibrary.reset(OpenSharedLibraryAndGetError(libName.c_str(), searchType, &loadError));
262     if (mTraceLibrary->getNative() == nullptr)
263     {
264         FATAL() << "Failed to load trace library (" << libName << "): " << loadError;
265     }
266 
267     callFunc<SetupEntryPoints>("SetupEntryPoints", static_cast<angle::TraceCallbacks *>(this),
268                                &mTraceFunctions);
269     mTraceFunctions->SetTraceInfo(traceInfo);
270     mTraceInfo = traceInfo;
271 }
272 
LoadBinaryData(const char * fileName)273 uint8_t *TraceLibrary::LoadBinaryData(const char *fileName)
274 {
275     std::ostringstream pathBuffer;
276     pathBuffer << mBinaryDataDir << "/" << fileName;
277     FILE *fp = fopen(pathBuffer.str().c_str(), "rb");
278     if (fp == 0)
279     {
280         fprintf(stderr, "Error loading binary data file: %s\n", fileName);
281         exit(1);
282     }
283     fseek(fp, 0, SEEK_END);
284     long size = ftell(fp);
285     fseek(fp, 0, SEEK_SET);
286     if (mTraceInfo.isBinaryDataCompressed)
287     {
288         if (!strstr(fileName, ".gz"))
289         {
290             fprintf(stderr, "Filename does not end in .gz");
291             exit(1);
292         }
293 
294         std::vector<uint8_t> compressedData(size);
295         size_t bytesRead = fread(compressedData.data(), 1, size, fp);
296         if (bytesRead != static_cast<size_t>(size))
297         {
298             std::cerr << "Failed to read binary data: " << bytesRead << " != " << size << "\n";
299             exit(1);
300         }
301 
302         if (!UncompressData(compressedData, &mBinaryData))
303         {
304             // Workaround for sporadic failures https://issuetracker.google.com/296921272
305             SaveDebugFile(mDebugOutputDir, fileName, ".gzdbg_input.gz", compressedData);
306             SaveDebugFile(mDebugOutputDir, fileName, ".gzdbg_attempt1", mBinaryData);
307             std::vector<uint8_t> uncompressedData;
308             bool secondResult = UncompressData(compressedData, &uncompressedData);
309             SaveDebugFile(mDebugOutputDir, fileName, ".gzdbg_attempt2", uncompressedData);
310             if (!secondResult)
311             {
312                 std::cerr << "Uncompress retry failed\n";
313                 exit(1);
314             }
315             std::cerr << "Uncompress retry succeeded, moving to mBinaryData\n";
316             mBinaryData = std::move(uncompressedData);
317         }
318     }
319     else
320     {
321         if (!strstr(fileName, ".angledata"))
322         {
323             fprintf(stderr, "Filename does not end in .angledata");
324             exit(1);
325         }
326         mBinaryData.resize(size + 1);
327         (void)fread(mBinaryData.data(), 1, size, fp);
328     }
329     fclose(fp);
330 
331     return mBinaryData.data();
332 }
333 
334 }  // namespace angle
335