]> git.cworth.org Git - vogl/blob - src/voglcommon/vogl_trace_file_reader.cpp
Initial vogl checkin
[vogl] / src / voglcommon / vogl_trace_file_reader.cpp
1 /**************************************************************************
2  *
3  * Copyright 2013-2014 RAD Game Tools and Valve Software
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  *
24  **************************************************************************/
25
26 // File: vogl_trace_file_reader.cpp
27 #include "vogl_trace_file_reader.h"
28 #include "vogl_console.h"
29 #include "vogl_file_utils.h"
30
31 vogl_trace_file_reader::trace_file_reader_status_t vogl_trace_file_reader::read_frame_packets(uint frame_index, uint num_frames, vogl_trace_packet_array &packets, uint &actual_frames_read)
32 {
33     VOGL_FUNC_TRACER
34
35     actual_frames_read = 0;
36
37     if (!is_opened())
38     {
39         vogl_error_printf("%s: Trace file is not open\n", VOGL_METHOD_NAME);
40
41         VOGL_ASSERT_ALWAYS;
42
43         return cFailed;
44     }
45
46     if (!num_frames)
47     {
48         actual_frames_read = 0;
49         return cOK;
50     }
51
52     vogl_scoped_location_saver saved_loc(*this);
53
54     if (!seek_to_frame(frame_index))
55     {
56         vogl_error_printf("%s: Failed seeking to frame %u\n", VOGL_METHOD_NAME, frame_index);
57         return cFailed;
58     }
59
60     uint total_frames_read = 0;
61
62     packets.reserve(packets.size() + num_frames * 1000);
63
64     trace_file_reader_status_t status = cOK;
65     for (;;)
66     {
67         status = read_next_packet();
68         if (status == cFailed)
69         {
70             vogl_error_printf("%s: Failed reading from trace file\n", VOGL_METHOD_NAME);
71             break;
72         }
73
74         packets.push_back(get_packet_buf());
75
76         if (is_eof_packet())
77             break;
78
79         if (is_swap_buffers_packet())
80         {
81             if (++total_frames_read == num_frames)
82                 break;
83         }
84     }
85
86     actual_frames_read = total_frames_read;
87
88     return cOK;
89 }
90
91 void vogl_trace_file_reader::create_eof_packet()
92 {
93     VOGL_FUNC_TRACER
94
95     vogl_trace_stream_packet_base eof_packet;
96     eof_packet.init(cTSPTEOF, sizeof(vogl_trace_stream_packet_base));
97     eof_packet.finalize();
98     m_packet_buf.resize(0);
99     m_packet_buf.append(reinterpret_cast<uint8 *>(&eof_packet), sizeof(eof_packet));
100 }
101
102 bool vogl_trace_file_reader::init_loose_file_blob_manager(const char *pTrace_filename, const char *pLoose_file_path)
103 {
104     VOGL_FUNC_TRACER
105
106     dynamic_string loose_file_path(".");
107
108     if ((pLoose_file_path) && vogl_strlen(pLoose_file_path))
109         loose_file_path = pLoose_file_path;
110     else if ((pTrace_filename) && (vogl_strlen(pTrace_filename)))
111     {
112         dynamic_string fname;
113         if (!file_utils::split_path(pTrace_filename, loose_file_path, fname))
114         {
115             console::error("%s: Failed splitting trace filename \"%s\", assuming \".\" as the loose file path\n", VOGL_METHOD_NAME, pTrace_filename);
116             loose_file_path = ".";
117         }
118     }
119
120     if (!m_loose_file_blob_manager.init(cBMFReadable, loose_file_path.get_ptr()))
121     {
122         close();
123         return false;
124     }
125
126     return true;
127 }
128
129 vogl_binary_trace_file_reader::vogl_binary_trace_file_reader()
130     : vogl_trace_file_reader(),
131       m_trace_file_size(0),
132       m_cur_frame_index(0),
133       m_max_frame_index(-1),
134       m_found_frame_file_offsets_packet(0)
135 {
136     VOGL_FUNC_TRACER
137
138     m_frame_file_offsets.reserve(4096);
139 }
140
141 vogl_binary_trace_file_reader::~vogl_binary_trace_file_reader()
142 {
143     VOGL_FUNC_TRACER
144
145     close();
146 }
147
148 bool vogl_binary_trace_file_reader::read_frame_file_offsets()
149 {
150     VOGL_FUNC_TRACER
151
152     m_frame_file_offsets.clear();
153     m_max_frame_index = -1;
154     m_found_frame_file_offsets_packet = false;
155
156     uint8_vec frame_offsets_data;
157     if (!m_archive_blob_manager.is_initialized() || !m_archive_blob_manager.get(VOGL_TRACE_ARCHIVE_FRAME_FILE_OFFSETS_FILENAME, frame_offsets_data))
158     {
159         vogl_debug_printf("%s: Couldn't find trace frame file offset file in trace archive, seeking will be slow in this trace file\n", VOGL_METHOD_NAME);
160         return false;
161     }
162
163     if (frame_offsets_data.size() & (sizeof(uint64_t) - 1))
164     {
165         vogl_error_printf("%s: Trace frame file offset file in trace archive is invalid, seeking will be slow in this trace file\n", VOGL_METHOD_NAME);
166         return false;
167     }
168
169     uint total_offsets = frame_offsets_data.size() / sizeof(uint64_t);
170
171     m_frame_file_offsets.resize(total_offsets);
172     memcpy(m_frame_file_offsets.get_ptr(), frame_offsets_data.get_ptr(), m_frame_file_offsets.size_in_bytes());
173
174     vogl_debug_printf("%s: Frame file offsets packet is OK, found %u total frame offsets\n", VOGL_METHOD_NAME, m_frame_file_offsets.size());
175
176     m_max_frame_index = m_frame_file_offsets.size() - 1;
177
178     m_found_frame_file_offsets_packet = true;
179     return true;
180 }
181
182 bool vogl_binary_trace_file_reader::open(const char *pFilename, const char *pLoose_file_path)
183 {
184     VOGL_FUNC_TRACER
185
186     close();
187
188     if (!init_loose_file_blob_manager(pFilename, pLoose_file_path))
189         return false;
190
191     if (!m_trace_stream.open(pFilename, cDataStreamReadable | cDataStreamSeekable, true))
192     {
193         close();
194         return false;
195     }
196
197     m_trace_file_size = m_trace_stream.get_size();
198
199     if (m_trace_stream.read(&m_sof_packet, sizeof(m_sof_packet)) != sizeof(m_sof_packet))
200     {
201         close();
202         return false;
203     }
204
205     if (!m_sof_packet.full_validation(sizeof(m_sof_packet)))
206     {
207         close();
208         return false;
209     }
210
211     if (m_sof_packet.m_version < static_cast<uint16>(VOGL_TRACE_FILE_VERSION))
212     {
213         vogl_error_printf("%s: Trace file version is not supported, found version 0x%04X, expected version 0x%04X!\n", VOGL_METHOD_NAME, m_sof_packet.m_version, VOGL_TRACE_FILE_VERSION);
214         return false;
215     }
216     else if (m_sof_packet.m_version > static_cast<uint16>(VOGL_TRACE_FILE_VERSION))
217     {
218         // TODO: Make this an error? Backwards compat?
219         vogl_warning_printf("%s: Trace file version is 0x%04X, expected version 0x%04X, this may not work at all!\n", VOGL_METHOD_NAME, m_sof_packet.m_version, VOGL_TRACE_FILE_VERSION);
220     }
221
222     if (m_sof_packet.m_archive_size)
223     {
224         if (!m_archive_blob_manager.init_file(cBMFReadable | cBMFOpenExisting, pFilename, m_sof_packet.m_archive_offset, m_sof_packet.m_archive_size))
225         {
226             vogl_error_printf("%s: Failed reading in-trace archive!\n", VOGL_METHOD_NAME);
227             return false;
228         }
229     }
230
231     m_packet_buf.reserve(512 * 1024);
232
233     m_trace_stream.seek(m_sof_packet.m_first_packet_offset, false);
234
235     if (!read_frame_file_offsets())
236     {
237         // Keep this in sync with the offset pushed in vogl_init_tracefile()!
238         m_frame_file_offsets.push_back(get_cur_file_ofs());
239     }
240
241     return true;
242 }
243
244 bool vogl_binary_trace_file_reader::is_opened()
245 {
246     VOGL_FUNC_TRACER
247
248     return m_trace_stream.is_opened();
249 }
250
251 const char *vogl_binary_trace_file_reader::get_filename()
252 {
253     VOGL_FUNC_TRACER
254
255     return m_trace_stream.get_name().get_ptr();
256 }
257
258 void vogl_binary_trace_file_reader::close()
259 {
260     VOGL_FUNC_TRACER
261
262     vogl_trace_file_reader::close();
263
264     m_trace_stream.close();
265     m_trace_file_size = 0;
266
267     m_cur_frame_index = 0;
268     m_max_frame_index = -1;
269     m_frame_file_offsets.resize(0);
270
271     m_saved_location_stack.clear();
272
273     m_found_frame_file_offsets_packet = false;
274 }
275
276 bool vogl_binary_trace_file_reader::is_at_eof()
277 {
278     VOGL_FUNC_TRACER
279
280     return (m_trace_stream.get_remaining() < sizeof(vogl_trace_stream_packet_base));
281 }
282
283 bool vogl_binary_trace_file_reader::seek_to_frame(uint frame_index)
284 {
285     VOGL_FUNC_TRACER
286
287     if (!is_opened())
288     {
289         VOGL_ASSERT_ALWAYS;
290         return false;
291     }
292
293     if (frame_index >= m_frame_file_offsets.size())
294     {
295         if ((m_max_frame_index >= 0) && (frame_index > m_max_frame_index))
296         {
297             vogl_warning_printf("%s: Can't seek to frame %u, max valid frame index is %u\n", VOGL_METHOD_NAME, frame_index, static_cast<uint>(m_max_frame_index));
298             return false;
299         }
300
301         vogl_warning_printf("%s: Seeking forward in binary trace from frame %u to frame %u, this could take a while to build the index\n", VOGL_METHOD_NAME, m_cur_frame_index, frame_index);
302
303         if (m_frame_file_offsets.size())
304         {
305             seek(m_frame_file_offsets.back());
306             m_cur_frame_index = m_frame_file_offsets.size() - 1;
307         }
308
309         do
310         {
311             trace_file_reader_status_t status = read_next_packet();
312             if (status == cFailed)
313             {
314                 vogl_error_printf("%s: Failed reading next packet\n", VOGL_METHOD_NAME);
315                 return false;
316             }
317
318             if ((status == cEOF) || (is_eof_packet()))
319                 break;
320
321         } while (frame_index >= m_frame_file_offsets.size());
322
323         if (frame_index >= m_frame_file_offsets.size())
324         {
325             if (g_command_line_params.get_value_as_bool("verbose"))
326                 vogl_debug_printf("%s: Failed seeking forward in binary trace to frame %u, cur frame is now %u\n", VOGL_METHOD_NAME, frame_index, m_cur_frame_index);
327             return false;
328         }
329     }
330
331     seek(m_frame_file_offsets[frame_index]);
332     m_cur_frame_index = frame_index;
333     return true;
334 }
335
336 int64_t vogl_binary_trace_file_reader::get_max_frame_index()
337 {
338     VOGL_FUNC_TRACER
339
340     if (m_max_frame_index < 0)
341     {
342         push_location();
343
344         seek_to_frame(0xFFFFFFFF);
345
346         pop_location();
347     }
348
349     if (m_max_frame_index >= 0)
350         return m_max_frame_index;
351     else
352         return -1;
353 }
354
355 vogl_trace_file_reader::trace_file_reader_status_t vogl_binary_trace_file_reader::read_next_packet()
356 {
357     VOGL_FUNC_TRACER
358
359     m_packet_buf.resize(sizeof(vogl_trace_stream_packet_base));
360
361     {
362         vogl_trace_stream_packet_base &packet_base = *reinterpret_cast<vogl_trace_stream_packet_base *>(m_packet_buf.get_ptr());
363         uint bytes_actually_read = m_trace_stream.read(&packet_base, sizeof(packet_base));
364         if (bytes_actually_read != sizeof(packet_base))
365         {
366             // Jam in a fake EOF packet in case the caller doesn't get the message that something is wrong
367             create_eof_packet();
368
369             // The could happen if the file was truncated, or the last packet didn't get entirely written.
370             if (bytes_actually_read)
371                 return cFailed;
372
373             if (m_max_frame_index < 0)
374                 m_max_frame_index = m_cur_frame_index;
375             else
376                 VOGL_ASSERT(m_max_frame_index == m_cur_frame_index);
377
378             return cEOF;
379         }
380
381         if ((!packet_base.basic_validation()) || (packet_base.m_size >= 0x7FFFFFFFULL))
382         {
383             console::error("%s: Bad trace file - packet failed basic validation tests!\n", VOGL_METHOD_NAME);
384
385             create_eof_packet();
386
387             return cFailed;
388         }
389
390         m_packet_buf.resize(packet_base.m_size);
391     }
392
393     vogl_trace_stream_packet_base &packet_base = *reinterpret_cast<vogl_trace_stream_packet_base *>(m_packet_buf.get_ptr());
394
395     uint num_bytes_remaining = packet_base.m_size - sizeof(vogl_trace_stream_packet_base);
396     if (num_bytes_remaining)
397     {
398         uint actual_bytes_read = m_trace_stream.read(m_packet_buf.get_ptr() + sizeof(vogl_trace_stream_packet_base), num_bytes_remaining);
399         if (actual_bytes_read != num_bytes_remaining)
400         {
401             console::error("%s: Failed reading variable size trace packet data (wanted %u bytes, got %u bytes), trace file is probably corrupted/invalid\n", VOGL_METHOD_NAME, num_bytes_remaining, actual_bytes_read);
402             return cFailed;
403         }
404     }
405
406     if (!packet_base.check_crc(packet_base.m_size))
407     {
408         console::error("%s: Bad trace file - packet CRC32 is bad!\n", VOGL_METHOD_NAME);
409
410         create_eof_packet();
411
412         return cFailed;
413     }
414
415     if (is_eof_packet())
416     {
417         if (m_max_frame_index < 0)
418             m_max_frame_index = m_cur_frame_index;
419         else
420             VOGL_ASSERT(m_max_frame_index == m_cur_frame_index);
421     }
422     else if (is_swap_buffers_packet())
423     {
424         m_cur_frame_index++;
425
426         if (m_cur_frame_index >= m_frame_file_offsets.size())
427         {
428             m_frame_file_offsets.resize(math::maximum(m_frame_file_offsets.size(), m_cur_frame_index + 1));
429             m_frame_file_offsets[m_cur_frame_index] = get_cur_file_ofs();
430         }
431         else
432         {
433             VOGL_ASSERT(m_frame_file_offsets[m_cur_frame_index] == get_cur_file_ofs());
434         }
435     }
436
437     return cOK;
438 }
439
440 bool vogl_binary_trace_file_reader::push_location()
441 {
442     VOGL_FUNC_TRACER
443
444     if (!m_trace_stream.is_opened())
445         return false;
446
447     saved_location *p = m_saved_location_stack.enlarge(1);
448     p->m_cur_frame_index = m_cur_frame_index;
449     p->m_cur_ofs = m_trace_stream.get_ofs();
450
451     return true;
452 }
453
454 bool vogl_binary_trace_file_reader::pop_location()
455 {
456     VOGL_FUNC_TRACER
457
458     if ((!m_trace_stream.is_opened()) || (m_saved_location_stack.is_empty()))
459         return false;
460
461     saved_location &loc = m_saved_location_stack.back();
462
463     bool success = true;
464
465     if (!m_trace_stream.seek(loc.m_cur_ofs, false))
466         success = false;
467     else
468         m_cur_frame_index = loc.m_cur_frame_index;
469
470     m_saved_location_stack.pop_back();
471
472     return success;
473 }
474
475 //-----------------------------------------------------------------------------
476 // vogl_json_trace_file_reader::vogl_json_trace_file_reader
477 //-----------------------------------------------------------------------------
478 vogl_json_trace_file_reader::vogl_json_trace_file_reader()
479     : vogl_trace_file_reader(),
480       m_filename_exists(false),
481       m_filename_is_in_multiframe_form(false),
482       m_cur_frame_index(0), m_max_frame_index(0), m_cur_packet_node_index(0), m_packet_node_size(0), m_doc_eof_key_value(false),
483       m_at_eof(false),
484       m_trace_packet(&m_trace_ctypes)
485 {
486     VOGL_FUNC_TRACER
487 }
488
489 vogl_json_trace_file_reader::~vogl_json_trace_file_reader()
490 {
491     VOGL_FUNC_TRACER
492
493     close();
494 }
495
496 bool vogl_json_trace_file_reader::open(const char *pFilename, const char *pLoose_file_path)
497 {
498     VOGL_FUNC_TRACER
499
500     close();
501
502     if (!init_loose_file_blob_manager(pFilename, pLoose_file_path))
503         return false;
504
505     m_filename = pFilename;
506     m_filename_is_in_multiframe_form = vogl_is_multiframe_json_trace_filename(pFilename);
507
508     if (!file_utils::split_path(pFilename, &m_drive, &m_dir, &m_fname, &m_ext))
509     {
510         console::error("%s: Failed splitting input filename: \"%s\"\n", VOGL_METHOD_NAME, pFilename);
511         close();
512         return false;
513     }
514
515     m_filename_exists = file_utils::does_file_exist(pFilename);
516     if ((!m_filename_exists) && (m_filename_is_in_multiframe_form))
517     {
518         console::error("%s: Could not open JSON trace file: \"%s\"\n", VOGL_METHOD_NAME, pFilename);
519         close();
520         return false;
521     }
522
523     if ((m_filename_exists) && (!m_filename_is_in_multiframe_form))
524     {
525         // Assume they just want to play one frame.
526         m_cur_frame_filename = m_filename;
527         m_base_filename = m_filename;
528         m_max_frame_index = 0;
529     }
530     else
531     {
532         uint i;
533         for (i = 0; i < 99999999; i++)
534         {
535             dynamic_string trial_base_name(m_fname);
536             if (m_filename_is_in_multiframe_form)
537                 trial_base_name.shorten(7);
538
539             if (!i)
540                 file_utils::combine_path(m_base_filename, m_drive.get_ptr(), m_dir.get_ptr(), trial_base_name.get_ptr());
541
542             dynamic_string trial_name(cVarArg, "%s_%06u", trial_base_name.get_ptr(), i);
543
544             dynamic_string trial_filename;
545             file_utils::combine_path_and_extension(trial_filename, m_drive.get_ptr(), m_dir.get_ptr(), trial_name.get_ptr(), m_ext.get_ptr());
546
547             if (!file_utils::does_file_exist(trial_filename.get_ptr()))
548                 break;
549
550             if (!i)
551                 m_cur_frame_filename = trial_filename;
552         }
553
554         if (!i)
555         {
556             console::error("%s: Could not open JSON trace file \"%s\"\n", VOGL_METHOD_NAME, pFilename);
557             close();
558             return false;
559         }
560
561         m_max_frame_index = i - 1;
562     }
563
564     if (!open_first_document())
565     {
566         close();
567         return false;
568     }
569
570     console::info("Opened JSON trace file \"%s\", frame range [%u - %u]\n", m_cur_frame_filename.get_ptr(), m_cur_frame_index, m_max_frame_index);
571
572     return true;
573 }
574
575 bool vogl_json_trace_file_reader::is_opened()
576 {
577     VOGL_FUNC_TRACER
578
579     return m_pPackets_array != NULL;
580 }
581
582 const char *vogl_json_trace_file_reader::get_filename()
583 {
584     VOGL_FUNC_TRACER
585
586     return m_filename.get_ptr();
587 }
588
589 bool vogl_json_trace_file_reader::open_first_document()
590 {
591     VOGL_FUNC_TRACER
592
593     if (!read_document(m_cur_frame_filename))
594         return false;
595
596     const json_node *pRoot_node = m_cur_doc.get_root();
597     if (!pRoot_node)
598     {
599         vogl_error_printf("%s: JSON file must be an object: \"%s\"\n", VOGL_METHOD_NAME, m_cur_frame_filename.get_ptr());
600         return false;
601     }
602
603     const json_node *pSOF_node = pRoot_node->find_child_object("sof");
604     if (!pSOF_node)
605     {
606         vogl_error_printf("%s: Failed finding SOF (start of file) packet in JSON file \"%s\"\n", VOGL_METHOD_NAME, m_cur_frame_filename.get_ptr());
607         return false;
608     }
609
610     int meta_pointer_sizes = pSOF_node->value_as_int32("pointer_sizes", -1);
611     if ((meta_pointer_sizes != sizeof(uint32)) && (meta_pointer_sizes != sizeof(uint64_t)))
612     {
613         vogl_error_printf("%s: Invalid meta pointer sizes field in JSON file \"%s\"\n", VOGL_METHOD_NAME, m_cur_frame_filename.get_ptr());
614         return false;
615     }
616
617     dynamic_string archive_filename = pSOF_node->value_as_string("archive_filename");
618     if (!archive_filename.is_empty())
619     {
620         dynamic_string full_archive_filename(archive_filename);
621
622         // Determine if the archive path is absolute or not
623         if (!full_archive_filename.contains('/') && !full_archive_filename.contains('\\'))
624         {
625             file_utils::combine_path(full_archive_filename, m_drive.get_ptr(), m_dir.get_ptr(), archive_filename.get_ptr());
626         }
627
628         if (!m_archive_blob_manager.init_file(cBMFReadable | cBMFOpenExisting, full_archive_filename.get_ptr()))
629         {
630             vogl_error_printf("%s: JSON trace relies on archive \"%s\", which cannot be opened! Will try to read anyway, but later operations may fail if loose files cannot be found.\n", VOGL_METHOD_NAME, archive_filename.get_ptr());
631             // Don't immediately exit in case they have manually deleted the archive and want everything to read from loose files.
632             //return false;
633         }
634     }
635
636     uint64_t trace_version = pSOF_node->value_as_uint64("version");
637
638     m_sof_packet.init();
639     m_sof_packet.m_pointer_sizes = meta_pointer_sizes;
640     m_sof_packet.m_version = trace_version;
641
642     if (m_archive_blob_manager.is_initialized())
643     {
644         file_utils::get_file_size(m_archive_blob_manager.get_archive_filename().get_ptr(), m_sof_packet.m_archive_size);
645     }
646
647     const json_node *pUUID_array = pSOF_node->find_child_array("uuid");
648     if (pUUID_array)
649     {
650         for (uint i = 0; i < math::minimum<uint>(pUUID_array->size(), vogl_trace_stream_start_of_file_packet::cUUIDSize); i++)
651             m_sof_packet.m_uuid[i] = pUUID_array->value_as_uint32(i);
652     }
653
654     // Makes no sense to finalize it, it's not complete or valid.
655     //m_sof_packet.finalize();
656
657     m_trace_ctypes.change_pointer_sizes(meta_pointer_sizes);
658
659     return true;
660 }
661
662 bool vogl_json_trace_file_reader::read_document(const dynamic_string &filename)
663 {
664     VOGL_FUNC_TRACER
665
666     m_cur_frame_filename = filename;
667
668     m_cur_packet_node_index = 0;
669     m_packet_node_size = 0;
670     m_doc_eof_key_value = 0;
671     m_pPackets_array = NULL;
672     m_cur_doc.clear();
673
674     bool deserialize_status = false;
675
676     // HACK HACK: to work around another app writing to the file as we try to read it in -endless mode
677     const uint cMaxRetries = 5;
678     for (uint tries = 0; tries < cMaxRetries; tries++)
679     {
680         if (!m_trace_stream.open(m_cur_frame_filename.get_ptr(), cDataStreamReadable, true))
681         {
682             console::error("%s: Could not open JSON trace file \"%s\"\n", VOGL_METHOD_NAME, m_cur_frame_filename.get_ptr());
683             return false;
684         }
685
686         deserialize_status = m_cur_doc.deserialize_file(m_cur_frame_filename.get_ptr());
687
688         m_trace_stream.close();
689
690         if (deserialize_status)
691             break;
692
693         // Sleep a while in case another app is writing to the file
694         vogl_sleep(500);
695     }
696
697     if (!deserialize_status)
698     {
699         if (m_cur_doc.get_error_msg().has_content())
700             vogl_error_printf("%s: Failed deserializing JSON file \"%s\"!\nError: %s Line: %u\n", VOGL_METHOD_NAME, m_cur_frame_filename.get_ptr(), m_cur_doc.get_error_msg().get_ptr(), m_cur_doc.get_error_line());
701         else
702             vogl_error_printf("%s: Failed deserializing JSON file \"%s\"!\n", VOGL_METHOD_NAME, m_cur_frame_filename.get_ptr());
703
704         m_cur_doc.clear();
705         return false;
706     }
707
708     vogl_message_printf("Processing JSON file \"%s\"\n", m_cur_frame_filename.get_ptr());
709
710     const json_node *pRoot_node = m_cur_doc.get_root();
711     if (!pRoot_node)
712     {
713         vogl_error_printf("%s: Couldn't find root node in JSON file \"%s\"\n", VOGL_METHOD_NAME, m_cur_frame_filename.get_ptr());
714         m_cur_doc.clear();
715         return false;
716     }
717
718     const json_node *pMeta_node = pRoot_node->find_child("meta");
719     if (!pMeta_node)
720     {
721         vogl_error_printf("%s: Couldn't find meta node in JSON file \"%s\"\n", VOGL_METHOD_NAME, m_cur_frame_filename.get_ptr());
722         m_cur_doc.clear();
723         return false;
724     }
725
726     int64_t meta_frame_index = pMeta_node->value_as_int64("cur_frame", -1);
727     if (meta_frame_index != m_cur_frame_index)
728     {
729         vogl_error_printf("%s: Invalid meta frame index in JSON file \"%s\" (expected %lli, got %lli)\n", VOGL_METHOD_NAME, m_cur_frame_filename.get_ptr(), static_cast<long long int>(m_cur_frame_index), static_cast<long long int>(meta_frame_index));
730         m_cur_doc.clear();
731         return false;
732     }
733
734     m_doc_eof_key_value = pMeta_node->value_as_int("eof", 0);
735
736     m_pPackets_array = pRoot_node->find_child_array("packets");
737     if (!m_pPackets_array)
738     {
739         vogl_error_printf("%s: Couldn't find packets node in JSON file \"%s\"\n", VOGL_METHOD_NAME, m_cur_frame_filename.get_ptr());
740         m_cur_doc.clear();
741         return false;
742     }
743
744     const json_node *pUUID_array = pMeta_node->find_child_array("uuid");
745     if (pUUID_array)
746     {
747         uint32 uuid[vogl_trace_stream_start_of_file_packet::cUUIDSize];
748         utils::zero_object(uuid);
749
750         for (uint i = 0; i < math::minimum<uint>(pUUID_array->size(), vogl_trace_stream_start_of_file_packet::cUUIDSize); i++)
751             uuid[i] = pUUID_array->value_as_uint32(i);
752
753         if (memcmp(uuid, m_sof_packet.m_uuid, sizeof(uuid)) != 0)
754         {
755             // Print the UUID's the way they would appear in the json for easier searching
756             vogl_warning_printf("%s: Document UUID (%u %u %u %u) in file %s differs from the UUID in the first frame's SOF packet (%u %u %u %u)\n", VOGL_METHOD_NAME,
757                                uuid[0], uuid[1], uuid[2], uuid[3],
758                                filename.get_ptr(),
759                                m_sof_packet.m_uuid[0], m_sof_packet.m_uuid[1], m_sof_packet.m_uuid[2], m_sof_packet.m_uuid[3]);
760         }
761     }
762
763     m_packet_node_size = m_pPackets_array->size();
764
765     return true;
766 }
767
768 void vogl_json_trace_file_reader::close()
769 {
770     VOGL_FUNC_TRACER
771
772     vogl_trace_file_reader::close();
773
774     m_filename.clear();
775     m_base_filename.clear();
776     m_filename_exists = false;
777     m_filename_is_in_multiframe_form = false;
778
779     m_drive.clear();
780     m_dir.clear();
781     m_fname.clear();
782     m_ext.clear();
783
784     m_cur_frame_filename.clear();
785
786     m_trace_stream.close();
787
788     m_cur_frame_index = 0;
789     m_max_frame_index = 0;
790
791     m_cur_doc.clear();
792     m_pPackets_array = NULL;
793     m_cur_packet_node_index = 0;
794     m_packet_node_size = 0;
795     m_doc_eof_key_value = 0;
796
797     m_at_eof = false;
798
799     m_saved_location_stack.clear();
800 }
801
802 bool vogl_json_trace_file_reader::is_at_eof()
803 {
804     VOGL_FUNC_TRACER
805
806     if (!m_cur_doc.get_root())
807         return true;
808
809     return (m_at_eof) ||
810            (m_cur_frame_index > m_max_frame_index) ||
811            ((m_doc_eof_key_value > 0) && (m_cur_packet_node_index >= m_packet_node_size));
812 }
813
814 dynamic_string vogl_json_trace_file_reader::compose_frame_filename()
815 {
816     VOGL_FUNC_TRACER
817
818     if ((m_filename_exists) && (!m_filename_is_in_multiframe_form))
819         return m_filename;
820
821     dynamic_string trial_base_name(m_fname);
822     if (m_filename_is_in_multiframe_form)
823         trial_base_name.shorten(7);
824
825     dynamic_string trial_name(cVarArg, "%s_%06u", trial_base_name.get_ptr(), m_cur_frame_index);
826
827     dynamic_string trial_filename;
828     file_utils::combine_path_and_extension(trial_filename, m_drive.get_ptr(), m_dir.get_ptr(), trial_name.get_ptr(), m_ext.get_ptr());
829
830     return trial_filename;
831 }
832
833 bool vogl_json_trace_file_reader::seek_to_frame(uint frame_index)
834 {
835     VOGL_FUNC_TRACER
836
837     // Special case: It's OK to seek to the very beginning of the frame just beyond the max valid JSON frame, to emulate how the binary read works.
838     if (frame_index == (m_max_frame_index + 1))
839     {
840         m_cur_frame_index = m_max_frame_index;
841
842         if (!read_document(compose_frame_filename()))
843             return false;
844
845         m_cur_packet_node_index = m_packet_node_size;
846
847         m_at_eof = true;
848
849         return true;
850     }
851     else if (frame_index > m_max_frame_index)
852     {
853         VOGL_ASSERT_ALWAYS;
854         return false;
855     }
856
857     m_cur_frame_index = frame_index;
858
859     if (!read_document(compose_frame_filename()))
860         return false;
861
862     m_at_eof = false;
863
864     return true;
865 }
866
867 vogl_trace_file_reader::trace_file_reader_status_t vogl_json_trace_file_reader::read_next_packet()
868 {
869     VOGL_FUNC_TRACER
870
871     if (!m_pPackets_array)
872     {
873         VOGL_ASSERT_ALWAYS;
874         return cFailed;
875     }
876
877     if ((m_at_eof) || (m_cur_frame_index > m_max_frame_index))
878     {
879         create_eof_packet();
880         return cEOF;
881     }
882
883     for (;;)
884     {
885         while (m_cur_packet_node_index >= m_packet_node_size)
886         {
887             if (m_doc_eof_key_value > 0)
888             {
889                 create_eof_packet();
890                 m_at_eof = true;
891                 return cEOF;
892             }
893
894             if (m_cur_frame_index <= m_max_frame_index)
895                 m_cur_frame_index++;
896
897             if (m_cur_frame_index > m_max_frame_index)
898             {
899                 vogl_warning_printf("%s: Last JSON document %u did not have a non-zero eof meta key, forcing EOF\n", VOGL_METHOD_NAME, m_max_frame_index);
900
901                 create_eof_packet();
902                 m_at_eof = true;
903                 return cEOF;
904             }
905
906             if (!read_document(compose_frame_filename()))
907                 return cFailed;
908         }
909
910         const json_node *pGL_node = m_pPackets_array->get_value_as_object(m_cur_packet_node_index);
911         if (!pGL_node)
912         {
913             vogl_warning_printf("%s: Ignoring invalid JSON key %s, file \"%s\"\n", VOGL_METHOD_NAME, m_pPackets_array->get_path_to_item(m_cur_packet_node_index).get_ptr(), m_cur_frame_filename.get_ptr());
914             m_cur_packet_node_index++;
915             continue;
916         }
917
918         bool success = m_trace_packet.json_deserialize(*pGL_node, m_cur_frame_filename.get_ptr(), &m_multi_blob_manager);
919         if (!success)
920         {
921             if (m_cur_doc.get_error_msg().has_content())
922                 vogl_error_printf("%s: Failed deserializing JSON file \"%s\"!\nError: %s Line: %u\n", VOGL_METHOD_NAME, m_cur_frame_filename.get_ptr(), m_cur_doc.get_error_msg().get_ptr(), m_cur_doc.get_error_line());
923             else
924                 vogl_error_printf("%s: Failed deserializing JSON file \"%s\"!\n", VOGL_METHOD_NAME, m_cur_frame_filename.get_ptr());
925             return cFailed;
926         }
927
928         m_dyn_stream.reset();
929         m_dyn_stream.open();
930
931         if (!m_trace_packet.serialize(m_dyn_stream))
932         {
933             vogl_error_printf("%s: Failed serializing binary trace packet data while processing JSON file \"%s\"!\n", VOGL_METHOD_NAME, m_cur_frame_filename.get_ptr());
934             return cFailed;
935         }
936
937         m_dyn_stream.get_buf().swap(m_packet_buf);
938
939         m_cur_packet_node_index++;
940
941         break;
942     }
943
944     return m_at_eof ? cEOF : cOK;
945 }
946
947 bool vogl_is_multiframe_json_trace_filename(const char *pFilename)
948 {
949     VOGL_FUNC_TRACER
950
951     dynamic_string drive, dir, fname, ext;
952     if (!file_utils::split_path(pFilename, &drive, &dir, &fname, &ext))
953         return false;
954
955     if ((fname.size() >= 7) && (fname.get_char_at_end(6) == '_'))
956     {
957         for (int i = 0; i < 6; i++)
958             if (!vogl_isdigit(fname.get_char_at_end(i)))
959                 return false;
960         return true;
961     }
962
963     return false;
964 }
965
966 bool vogl_json_trace_file_reader::push_location()
967 {
968     VOGL_FUNC_TRACER
969
970     if (!m_pPackets_array)
971         return false;
972
973     saved_location *p = m_saved_location_stack.enlarge(1);
974     p->m_cur_frame_filename = m_cur_frame_filename;
975     p->m_cur_frame_index = m_cur_frame_index;
976     p->m_cur_packet_node_index = m_cur_packet_node_index;
977     p->m_at_eof = m_at_eof;
978
979     return true;
980 }
981
982 bool vogl_json_trace_file_reader::pop_location()
983 {
984     VOGL_FUNC_TRACER
985
986     if ((!m_pPackets_array) || (m_saved_location_stack.is_empty()))
987         return false;
988
989     saved_location &loc = m_saved_location_stack.back();
990
991     bool success = true;
992
993     if ((loc.m_cur_frame_filename != m_cur_frame_filename) || (loc.m_cur_frame_index != m_cur_frame_index))
994     {
995         m_cur_frame_index = loc.m_cur_frame_index;
996
997         if (!read_document(loc.m_cur_frame_filename))
998             success = false;
999     }
1000
1001     if (success)
1002     {
1003         m_cur_frame_index = loc.m_cur_frame_index;
1004
1005         m_at_eof = loc.m_at_eof;
1006
1007         if (loc.m_cur_packet_node_index > m_pPackets_array->size())
1008             success = false;
1009         else
1010             m_cur_packet_node_index = loc.m_cur_packet_node_index;
1011     }
1012
1013     m_saved_location_stack.pop_back();
1014
1015     return success;
1016 }
1017
1018 vogl_trace_file_reader_type_t vogl_determine_trace_file_type(const dynamic_string &orig_filename, dynamic_string &filename_to_use)
1019 {
1020     VOGL_FUNC_TRACER
1021
1022     dynamic_string trace_extension(orig_filename);
1023     file_utils::get_extension(trace_extension);
1024
1025     vogl_trace_file_reader_type_t trace_reader_type = cINVALID_TRACE_FILE_READER;
1026
1027     if (trace_extension == "bin")
1028     {
1029         trace_reader_type = cBINARY_TRACE_FILE_READER;
1030         filename_to_use = orig_filename;
1031     }
1032     else if (trace_extension == "json")
1033     {
1034         trace_reader_type = cJSON_TRACE_FILE_READER;
1035         filename_to_use = orig_filename;
1036     }
1037     else if (file_utils::does_file_exist(orig_filename.get_ptr()))
1038     {
1039         FILE *pFile = vogl_fopen(orig_filename.get_ptr(), "rb");
1040         if (pFile)
1041         {
1042             int first_char = fgetc(pFile);
1043             if (first_char == '{')
1044             {
1045                 trace_reader_type = cJSON_TRACE_FILE_READER;
1046                 filename_to_use = orig_filename;
1047             }
1048             else if (first_char != EOF)
1049             {
1050                 vogl_trace_stream_packet_base first_packet;
1051                 utils::zero_object(first_packet);
1052
1053                 *reinterpret_cast<char *>(&first_packet) = static_cast<char>(first_char);
1054
1055                 if (fread(reinterpret_cast<char *>(&first_packet) + 1, sizeof(first_packet) - 1, 1, pFile) == 1)
1056                 {
1057                     if ((first_packet.m_prefix == vogl_trace_stream_packet_base::cTracePacketPrefix) && (first_packet.m_type == cTSPTSOF))
1058                     {
1059                         trace_reader_type = cBINARY_TRACE_FILE_READER;
1060                         filename_to_use = orig_filename;
1061                     }
1062                 }
1063             }
1064
1065             vogl_fclose(pFile);
1066         }
1067     }
1068
1069     if (trace_reader_type == cINVALID_TRACE_FILE_READER)
1070     {
1071         dynamic_string trial_filename(cVarArg, "%s.bin", orig_filename.get_ptr());
1072         if (file_utils::does_file_exist(trial_filename.get_ptr()))
1073         {
1074             trace_reader_type = cBINARY_TRACE_FILE_READER;
1075             filename_to_use = trial_filename;
1076         }
1077         else
1078         {
1079             trial_filename.format("%s.json", orig_filename.get_ptr());
1080             if (file_utils::does_file_exist(trial_filename.get_ptr()))
1081             {
1082                 trace_reader_type = cJSON_TRACE_FILE_READER;
1083                 filename_to_use = trial_filename;
1084             }
1085             else
1086             {
1087                 trial_filename.format("%s_000000.json", orig_filename.get_ptr());
1088                 if (file_utils::does_file_exist(trial_filename.get_ptr()))
1089                 {
1090                     trace_reader_type = cJSON_TRACE_FILE_READER;
1091                     filename_to_use = trial_filename;
1092                 }
1093             }
1094         }
1095     }
1096
1097     return trace_reader_type;
1098 }
1099
1100 vogl_trace_file_reader *vogl_create_trace_file_reader(vogl_trace_file_reader_type_t trace_type)
1101 {
1102     VOGL_FUNC_TRACER
1103
1104     switch (trace_type)
1105     {
1106         case cBINARY_TRACE_FILE_READER:
1107             return vogl_new(vogl_binary_trace_file_reader);
1108             break;
1109         case cJSON_TRACE_FILE_READER:
1110             return vogl_new(vogl_json_trace_file_reader);
1111             break;
1112         default:
1113         {
1114             VOGL_ASSERT_ALWAYS;
1115             break;
1116         }
1117     }
1118
1119     return NULL;
1120 }
1121
1122 vogl_trace_file_reader *vogl_open_trace_file(dynamic_string &orig_filename, dynamic_string &actual_filename, const char *pLoose_file_path)
1123 {
1124     VOGL_FUNC_TRACER
1125
1126     vogl_trace_file_reader_type_t trace_type = vogl_determine_trace_file_type(orig_filename, actual_filename);
1127     if (trace_type == cINVALID_TRACE_FILE_READER)
1128     {
1129         vogl_error_printf("%s: Unable to determine file type of trace file \"%s\"\n", VOGL_FUNCTION_NAME, orig_filename.get_ptr());
1130         return NULL;
1131     }
1132
1133     vogl_trace_file_reader *pTrace_reader = vogl_create_trace_file_reader(trace_type);
1134     if (!pTrace_reader)
1135     {
1136         vogl_error_printf("%s: Unable to determine file type of trace file \"%s\"\n", VOGL_FUNCTION_NAME, orig_filename.get_ptr());
1137         return NULL;
1138     }
1139
1140     if (!pTrace_reader->open(actual_filename.get_ptr(), pLoose_file_path))
1141     {
1142         vogl_error_printf("%s: Failed opening trace file \"%s\"\n", VOGL_FUNCTION_NAME, orig_filename.get_ptr());
1143         vogl_delete(pTrace_reader);
1144         return NULL;
1145     }
1146
1147     return pTrace_reader;
1148 }