1 /**************************************************************************
3 * Copyright 2013-2014 RAD Game Tools and Valve Software
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:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
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
24 **************************************************************************/
26 // File: vogl_replay_tool.cpp
27 #include "vogl_common.h"
28 #include "vogl_gl_replayer.h"
29 #include "vogl_texture_format.h"
30 #include "vogl_trace_file_writer.h"
32 #include "vogl_colorized_console.h"
33 #include "vogl_command_line_params.h"
34 #include "vogl_cfile_stream.h"
35 #include "vogl_value.h"
36 #include "vogl_dynamic_stream.h"
37 #include "vogl_file_utils.h"
38 #include "vogl_mergesort.h"
39 #include "vogl_unique_ptr.h"
40 #include "vogl_find_files.h"
41 #include "vogl_bigint128.h"
42 #include "vogl_regex.h"
44 #include <sys/types.h>
47 #include "vogl_json.h"
48 #include "vogl_blob_manager.h"
51 #include "vogl_remote.h"
52 #endif // VOGL_REMOTING
54 #include "libtelemetry.h"
57 #include <X11/Xutil.h>
60 //----------------------------------------------------------------------------------------------------------------------
62 //----------------------------------------------------------------------------------------------------------------------
63 static void *g_actual_libgl_module_handle;
64 static cfile_stream *g_vogl_pLog_stream;
66 //----------------------------------------------------------------------------------------------------------------------
67 // command line params
68 //----------------------------------------------------------------------------------------------------------------------
69 static command_line_param_desc g_command_line_param_descs[] =
71 { "replay", 0, false, "Replay mode (the default), must specify .BIN or .JSON trace file to replay" },
72 { "dump", 0, false, "Dump mode: Dumps binary trace file to a JSON trace file, must specify input and output filenames" },
73 { "parse", 0, false, "Parse mode: Parse JSON trace file to a binary trace file, must specify input and output filenames" },
74 { "info", 0, false, "Info mode: Output statistics about a trace file" },
75 { "unpack_json", 0, false, "Unpack UBJ to JSON mode: Unpack UBJ (Universal Binary JSON) to textual JSON, must specify input and output filenames" },
76 { "pack_json", 0, false, "Pack JSON to UBJ mode: Pack textual JSON to UBJ, must specify input and output filenames" },
77 { "find", 0, false, "Find all calls with parameters containing a specific value, combine with -find_param, -find_func, find_namespace, etc. params" },
78 { "compare_hash_files", 0, false, "Compare two files containing CRC's or per-component sums (presumably written using dump_backbuffer_hashes)" },
81 { "width", 1, false, "Replay: Set initial window width (default is 1024)" },
82 { "height", 1, false, "Replay: Set initial window height (default is 768)" },
83 { "lock_window_dimensions", 0, false, "Replay: Don't automatically change window's dimensions during replay" },
84 { "trim_file", 1, false, "Replay: Create a trimmed trace file during replay, must also specify -trim_frame" },
85 { "trim_frame", 1, false, "Replay: Frame index to begin trim, 0=beginning of trace, 1=first API call after first swap, etc." },
86 { "trim_len", 1, false, "Replay: Length of trim file, default=1 frame" },
87 { "multitrim", 0, false, "Replay trimming: Trim each frame to a different file" },
88 { "multitrim_interval", 1, false, "Replay trimming: Set the # of frames between each multitrimmed frame (default is 1)" },
89 { "no_trim_optimization", 0, false, "Replay trimming: If specified, do not remove unused programs, shaders, etc. from trim file" },
90 { "trim_call", 1, false, "Replay: Call counter index to begin trim" },
91 { "write_snapshot_call", 1, false, "Replay: Write JSON snapshot at the specified call counter index" },
92 { "write_snapshot_file", 1, false, "Replay: Write JSON snapshot to specified filename, must also specify --write_snapshot_call" },
93 { "write_snapshot_blobs", 0, false, "Replay: Write JSON snapshot blob files, must also specify --write_snapshot_call" },
94 { "endless", 0, false, "Replay: Loop replay endlessly instead of exiting" },
95 { "hash_backbuffer", 0, false, "Replay: Hash and output backbuffer CRC before every swap" },
96 { "dump_backbuffer_hashes", 1, false, "Replay: Dump backbuffer hashes to a text file" },
97 { "sum_hashing", 0, false, "Replay: Use per-component sums, instead of CRC hashing (useful for multisampling)" },
98 { "dump_screenshots", 0, false, "Replay: Dump backbuffer screenshot before every swap to numbered PNG files" },
99 { "dump_screenshots_prefix", 1, false, "Replay: Set PNG screenshot file prefix" },
100 { "swap_sleep", 1, false, "Replay: Sleep for X milliseconds after every swap" },
101 { "dump_packets_on_error", 0, false, "Replay: Dump GL trace packets as JSON to stdout on replay errors" },
102 { "dump_packet_blob_files_on_error", 0, false, "Replay: Used with -dump_packets_on_error, also dumps all binary blob files associated with each packet" },
103 { "dump_all_packets", 0, false, "Replay: Dump all GL trace packets as JSON to stdout" },
104 { "dump_shaders_on_draw", 0, false, "Replay: Dump shader source on draw calls" },
105 { "dump_framebuffer_on_draw", 0, false, "Replay: Dump framebuffer to PNG files after each draw/glEnd/glCallList" },
106 { "dump_framebuffer_on_draw_prefix", 1, false, "Replay: Base path/filename to use for --dump_framebuffer_on_draw" },
107 { "dump_framebuffer_on_draw_frame", 1, false, "Replay: Limit dumping framebuffer PNG files" },
108 { "dump_framebuffer_on_draw_first_gl_call", 1, false, "Replay: Limit dumping framebuffer PNG files" },
109 { "dump_framebuffer_on_draw_last_gl_call", 1, false, "Replay: Limit dumping framebuffer PNG files" },
110 { "force_debug_context", 0, false, "Replay: Force GL debug contexts" },
111 { "pause_on_exit", 0, false, "Replay: Wait for a keypress on exit" },
112 { "debug_test_snapshot_serialization", 0, false, "Interactive Replay Mode: Immediately serialize/deserialize state snapshots after taking them" },
113 { "pause_on_frame", 1, false, "Replay interactive mode: Pause on specified frame" },
114 { "interactive", 0, false, "Replay mode: Enable keyboard keys" },
115 { "disable_snapshot_caching", 0, false, "Replay mode: Disable caching of all state snapshot files, so they can be manually modified during replay" },
116 { "benchmark", 0, false, "Replay mode: Disable glGetError()'s, divergence checks, during replaying" },
117 { "keyframe_base_filename", 1, false, "Replay: Set base filename of trimmed replay keyframes, used for fast seeking" },
119 { "telemetry_level", 1, false, "Set Telemetry level." },
121 { "loop_frame", 1, false, "Replay: loop mode's start frame" },
122 { "loop_len", 1, false, "Replay: loop mode's loop length" },
123 { "loop_count", 1, false, "Replay: loop mode's loop count" },
124 { "draw_kill_max_thresh", 1, false, "Replay: Enable draw kill mode during looping to visualize order of draws, sets the max # of draws before counter resets to 0" },
127 { "find_func", 1, false, "Find: Limit the find to only the specified function name POSIX regex pattern" },
128 { "find_param", 1, false, "Find: The parameter value to find, hex, decimal integers, or GL enum strings OK" },
129 { "find_namespace", 1, false, "Find: Optionally limits -handle to only parameters using the specified handle namespace: invalid, GLhandleARB, GLframebuffer, GLtexture, GLrenderbuffer, GLquery, GLsampler, GLprogramARB, GLprogram, GLarray, GLlist, GLlocation, GLlocationARB, GLfence, GLsync, GLpipeline, GLshader, GLbuffer, GLfeedback, GLarrayAPPLE, GLfragmentShaderATI" },
130 { "find_param_name", 1, false, "Find: Optionally limits the find to only params with the specified name (specify \"return\" to limit search to only return values)" },
131 { "find_frame_low", 1, false, "Find: Optionally limit the find to frames beginning at the specified frame index" },
132 { "find_frame_high", 1, false, "Find: Optionally limit the find to frames up to and including the specified frame index" },
133 { "find_call_low", 1, false, "Find: Optionally limit the find to GL calls beginning at the specified call index" },
134 { "find_call_high", 1, false, "Find: Optionally limit the find to GL calls up to and including the specified call index" },
136 // compare_hash_files specific
137 { "sum_compare_threshold", 1, false, "compare_hash_files: Only report mismatches greater than the specified threshold, use with --sum_hashing" },
138 { "compare_ignore_frames", 1, false, "compare_hash_files: Ignore first X frames" },
139 { "compare_expected_frames", 1, false, "compare_hash_files: Fail if the # of frames is not X" },
140 { "compare_first_frame", 1, false, "compare_hash_files: First frame to compare to in second hash file" },
141 { "ignore_line_count_differences", 0, false, "compare_hash_files: Don't stop if the # of lines differs between the two files" },
144 { "verify", 0, false, "Dump: Fully round-trip verify all JSON objects vs. the original packet's" },
145 { "no_blobs", 0, false, "Dump: Don't write binary blob files" },
146 { "write_debug_info", 0, false, "Dump: Write extra debug info to output JSON trace files" },
147 { "loose_file_path", 1, false, "Prefer reading trace blob files from this directory vs. the archive referred to or present in the trace file" },
148 { "debug", 0, false, "Enable verbose debug information" },
149 { "logfile", 1, false, "Create logfile" },
150 { "logfile_append", 1, false, "Append output to logfile" },
151 { "help", 0, false, "Display this help" },
152 { "?", 0, false, "Display this help" },
153 { "replay_debug", 0, false, "Enable various debug/verification code in the replayer" },
154 { "pause", 0, false, "Wait for a key at startup (so a debugger can be attached)" },
155 { "verbose", 0, false, "Verbose debug output" },
156 { "quiet", 0, false, "Disable all console output" },
157 { "gl_debug_log", 0, false, "Dump GL prolog/epilog messages to stdout (very slow - helpful to narrow down driver crashes)" },
158 { "vogl_func_tracing", 0, false, NULL },
161 static command_line_param_desc g_command_line_interactive_descs[] =
163 { "s", 0, false, "slow mode" },
164 { "<space>", 0, false, "pause" },
165 { "r", 0, false, "rewind to beginning" },
166 { "e", 0, false, "seek to last frame" },
167 { "t", 0, false, "trim frame" },
168 { "j", 0, false, "trim and play json" },
169 { "<left>", 0, false, "step left" },
170 { "<right>", 0, false, "step right" },
173 //----------------------------------------------------------------------------------------------------------------------
175 //----------------------------------------------------------------------------------------------------------------------
176 static bool init_logfile()
180 dynamic_string backbuffer_hash_file;
181 if (g_command_line_params.get_value_as_string(backbuffer_hash_file, "dump_backbuffer_hashes"))
183 remove(backbuffer_hash_file.get_ptr());
184 vogl_message_printf("Deleted backbuffer hash file \"%s\"\n", backbuffer_hash_file.get_ptr());
187 dynamic_string log_file(g_command_line_params.get_value_as_string_or_empty("logfile"));
188 dynamic_string log_file_append(g_command_line_params.get_value_as_string_or_empty("logfile_append"));
189 if (log_file.is_empty() && log_file_append.is_empty())
192 dynamic_string filename(log_file_append.is_empty() ? log_file : log_file_append);
194 // This purposely leaks, don't care
195 g_vogl_pLog_stream = vogl_new(cfile_stream);
197 if (!g_vogl_pLog_stream->open(filename.get_ptr(), cDataStreamWritable, !log_file_append.is_empty()))
199 vogl_error_printf("%s: Failed opening log file \"%s\"\n", VOGL_FUNCTION_NAME, filename.get_ptr());
204 vogl_message_printf("Opened log file \"%s\"\n", filename.get_ptr());
206 console::set_log_stream(g_vogl_pLog_stream);
212 //----------------------------------------------------------------------------------------------------------------------
214 //----------------------------------------------------------------------------------------------------------------------
215 static void tool_print_title()
219 printf("voglreplay ");
220 if (sizeof(void *) > 4)
221 console::printf("64-bit ");
223 console::printf("32-bit ");
224 #ifdef VOGL_BUILD_DEBUG
225 console::printf("Debug ");
227 console::printf("Release ");
229 console::printf("Built %s %s\n", __DATE__, __TIME__);
232 //----------------------------------------------------------------------------------------------------------------------
234 //----------------------------------------------------------------------------------------------------------------------
235 static void tool_print_help()
239 console::printf("Usage: voglreplay [ -option ... ] input_file optional_output_file [ -option ... ]\n");
240 console::printf("Command line options may begin with single minus \"-\" or double minus \"--\"\n");
242 console::printf("\nCommand line options:\n");
244 dump_command_line_info(VOGL_ARRAY_SIZE(g_command_line_param_descs), g_command_line_param_descs, "--");
246 console::printf("\nInteractive replay mode keys:\n");
247 dump_command_line_info(VOGL_ARRAY_SIZE(g_command_line_interactive_descs), g_command_line_interactive_descs, " ");
250 //----------------------------------------------------------------------------------------------------------------------
251 // init_command_line_params
252 //----------------------------------------------------------------------------------------------------------------------
253 static bool init_command_line_params(int argc, char *argv[])
257 command_line_params::parse_config parse_cfg;
258 parse_cfg.m_single_minus_params = true;
259 parse_cfg.m_double_minus_params = true;
261 if (!g_command_line_params.parse(get_command_line_params(argc, argv), VOGL_ARRAY_SIZE(g_command_line_param_descs), g_command_line_param_descs, parse_cfg))
263 vogl_error_printf("%s: Failed parsing command line parameters!\n", VOGL_FUNCTION_NAME);
270 if (g_command_line_params.get_value_as_bool("help") || g_command_line_params.get_value_as_bool("?"))
279 //----------------------------------------------------------------------------------------------------------------------
281 //----------------------------------------------------------------------------------------------------------------------
282 static bool load_gl()
286 g_actual_libgl_module_handle = dlopen("libGL.so.1", RTLD_LAZY);
287 if (!g_actual_libgl_module_handle)
289 vogl_error_printf("%s: Failed loading libGL.so.1!\n", VOGL_FUNCTION_NAME);
293 GL_ENTRYPOINT(glXGetProcAddress) = reinterpret_cast<glXGetProcAddress_func_ptr_t>(dlsym(g_actual_libgl_module_handle, "glXGetProcAddress"));
294 if (!GL_ENTRYPOINT(glXGetProcAddress))
296 vogl_error_printf("%s: Failed getting address of glXGetProcAddress() from libGL.so.1!\n", VOGL_FUNCTION_NAME);
303 //----------------------------------------------------------------------------------------------------------------------
304 // vogl_get_proc_address_helper
305 //----------------------------------------------------------------------------------------------------------------------
306 static vogl_void_func_ptr_t vogl_get_proc_address_helper(const char *pName)
310 vogl_void_func_ptr_t pFunc = g_actual_libgl_module_handle ? reinterpret_cast<vogl_void_func_ptr_t>(dlsym(g_actual_libgl_module_handle, pName)) : NULL;
312 if ((!pFunc) && (GL_ENTRYPOINT(glXGetProcAddress)))
313 pFunc = reinterpret_cast<vogl_void_func_ptr_t>(GL_ENTRYPOINT(glXGetProcAddress)(reinterpret_cast<const GLubyte *>(pName)));
319 // HACK HACK - for testing
320 static void vogl_direct_gl_func_prolog(gl_entrypoint_id_t entrypoint_id, void *pUser_data, void **pStack_data)
322 printf("*** PROLOG %u\n", entrypoint_id);
325 void vogl_direct_gl_func_epilog(gl_entrypoint_id_t entrypoint_id, void *pUser_data, void **pStack_data)
327 printf("*** EPILOG %u\n", entrypoint_id);
331 //----------------------------------------------------------------------------------------------------------------------
332 // vogl_direct_gl_func_prolog - This function is called before EVERY single GL/GLX function call we make.
333 //----------------------------------------------------------------------------------------------------------------------
334 static void vogl_direct_gl_func_prolog(gl_entrypoint_id_t entrypoint_id, void *pUser_data, void **ppStack_data)
336 VOGL_NOTE_UNUSED(entrypoint_id);
337 VOGL_NOTE_UNUSED(pUser_data);
338 VOGL_NOTE_UNUSED(ppStack_data);
340 printf("* GLPROLOG %s\n", g_vogl_entrypoint_descs[entrypoint_id].m_pName);
343 //----------------------------------------------------------------------------------------------------------------------
344 // vogl_direct_gl_func_epilog - This function is called immediately after EVERY single GL/GLX function call we make.
345 //----------------------------------------------------------------------------------------------------------------------
346 static void vogl_direct_gl_func_epilog(gl_entrypoint_id_t entrypoint_id, void *pUser_data, void **ppStack_data)
348 VOGL_NOTE_UNUSED(entrypoint_id);
349 VOGL_NOTE_UNUSED(pUser_data);
350 VOGL_NOTE_UNUSED(ppStack_data);
352 printf("* GLEPILOG %s\n", g_vogl_entrypoint_descs[entrypoint_id].m_pName);
355 //----------------------------------------------------------------------------------------------------------------------
357 //----------------------------------------------------------------------------------------------------------------------
358 static bool vogl_replay_init(int argc, char *argv[])
362 g_thread_safe_random.seed_from_urandom();
364 colorized_console::init();
365 colorized_console::set_exception_callback();
366 //console::set_tool_prefix("(voglreplay) ");
370 if (!init_command_line_params(argc, argv))
374 int telemetry_level = g_command_line_params.get_value_as_int("telemetry_level", 0,
375 TELEMETRY_LEVEL_MIN + 1, TELEMETRY_LEVEL_MIN, TELEMETRY_LEVEL_MAX);
376 telemetry_set_level(telemetry_level);
380 vogl_common_lib_early_init();
381 vogl_common_lib_global_init();
383 if (g_command_line_params.get_value_as_bool("quiet"))
384 console::disable_output();
386 if (g_command_line_params.get_value_as_bool("gl_debug_log"))
388 vogl_set_direct_gl_func_prolog(vogl_direct_gl_func_prolog, NULL);
389 vogl_set_direct_gl_func_epilog(vogl_direct_gl_func_epilog, NULL);
395 bool wrap_all_gl_calls = true;
397 if (g_command_line_params.get_value_as_bool("benchmark"))
398 wrap_all_gl_calls = false;
400 vogl_init_actual_gl_entrypoints(vogl_get_proc_address_helper, wrap_all_gl_calls);
403 // HACK HACK - for testing
404 vogl_set_direct_gl_func_prolog(vogl_direct_gl_func_prolog, NULL);
405 vogl_set_direct_gl_func_epilog(vogl_direct_gl_func_epilog, NULL);
411 //----------------------------------------------------------------------------------------------------------------------
412 // vogl_replay_deinit
413 //----------------------------------------------------------------------------------------------------------------------
414 static void vogl_replay_deinit()
418 colorized_console::deinit();
421 //----------------------------------------------------------------------------------------------------------------------
422 // X11_Pending - from SDL
423 //----------------------------------------------------------------------------------------------------------------------
424 static int X11_Pending(Display *display)
428 /* Flush the display connection and look to see if events are queued */
430 if (XEventsQueued(display, QueuedAlready))
435 /* More drastic measures are required -- see if X is ready to talk */
437 static struct timeval zero_time; /* static == 0 */
441 x11_fd = ConnectionNumber(display);
443 FD_SET(x11_fd, &fdset);
444 if (select(x11_fd + 1, &fdset, NULL, NULL, &zero_time) == 1)
446 return (XPending(display));
450 /* Oh well, nothing is ready .. */
454 //----------------------------------------------------------------------------------------------------------------------
455 // read_state_snapshot_from_trace
456 //----------------------------------------------------------------------------------------------------------------------
457 static vogl_gl_state_snapshot *read_state_snapshot_from_trace(dynamic_string filename)
461 timed_scope ts(VOGL_FUNCTION_NAME);
463 vogl_gl_state_snapshot *pSnapshot = NULL;
465 vogl_loose_file_blob_manager file_blob_manager;
466 dynamic_string keyframe_trace_path(file_utils::get_pathname(filename.get_ptr()));
467 file_blob_manager.init(cBMFReadable, keyframe_trace_path.get_ptr());
469 dynamic_string actual_keyframe_filename;
470 vogl_unique_ptr<vogl_trace_file_reader> pTrace_reader(vogl_open_trace_file(filename, actual_keyframe_filename, NULL));
471 if (!pTrace_reader.get())
473 vogl_error_printf("%s: Failed reading keyframe file %s!\n", VOGL_FUNCTION_NAME, filename.get_ptr());
477 vogl_ctypes trace_gl_ctypes(pTrace_reader->get_sof_packet().m_pointer_sizes);
479 vogl_trace_packet keyframe_trace_packet(&trace_gl_ctypes);
481 bool found_snapshot = false;
484 vogl_trace_file_reader::trace_file_reader_status_t read_status = pTrace_reader->read_next_packet();
486 if ((read_status != vogl_trace_file_reader::cOK) && (read_status != vogl_trace_file_reader::cEOF))
488 vogl_error_printf("%s: Failed reading from keyframe trace file!\n", VOGL_FUNCTION_NAME);
492 if ((read_status == vogl_trace_file_reader::cEOF) || (pTrace_reader->get_packet_type() == cTSPTEOF))
494 vogl_error_printf("%s: Failed finding state snapshot in keyframe file!\n", VOGL_FUNCTION_NAME);
498 if (pTrace_reader->get_packet_type() != cTSPTGLEntrypoint)
501 if (!keyframe_trace_packet.deserialize(pTrace_reader->get_packet_buf().get_ptr(), pTrace_reader->get_packet_buf().size(), false))
503 vogl_error_printf("%s: Failed parsing GL entrypoint packet in keyframe file\n", VOGL_FUNCTION_NAME);
507 const vogl_trace_gl_entrypoint_packet *pGL_packet = &pTrace_reader->get_packet<vogl_trace_gl_entrypoint_packet>();
508 gl_entrypoint_id_t entrypoint_id = static_cast<gl_entrypoint_id_t>(pGL_packet->m_entrypoint_id);
510 //const gl_entrypoint_desc_t &entrypoint_desc = g_vogl_entrypoint_descs[entrypoint_id];
512 if (vogl_is_swap_buffers_entrypoint(entrypoint_id) || vogl_is_draw_entrypoint(entrypoint_id) || vogl_is_make_current_entrypoint(entrypoint_id))
514 vogl_error_printf("Failed finding state snapshot in keyframe file!\n");
518 switch (entrypoint_id)
520 case VOGL_ENTRYPOINT_glInternalTraceCommandRAD:
522 GLuint cmd = keyframe_trace_packet.get_param_value<GLuint>(0);
523 GLuint size = keyframe_trace_packet.get_param_value<GLuint>(1);
524 VOGL_NOTE_UNUSED(size);
526 if (cmd == cITCRKeyValueMap)
528 key_value_map &kvm = keyframe_trace_packet.get_key_value_map();
530 dynamic_string cmd_type(kvm.get_string("command_type"));
531 if (cmd_type == "state_snapshot")
533 dynamic_string id(kvm.get_string("binary_id"));
536 vogl_error_printf("%s: Missing binary_id field in glInternalTraceCommandRAD key_valye_map command type: \"%s\"\n", VOGL_FUNCTION_NAME, cmd_type.get_ptr());
540 uint8_vec snapshot_data;
542 timed_scope ts("get_multi_blob_manager().get");
543 if (!pTrace_reader->get_multi_blob_manager().get(id, snapshot_data) || (snapshot_data.is_empty()))
545 vogl_error_printf("%s: Failed reading snapshot blob data \"%s\"!\n", VOGL_FUNCTION_NAME, id.get_ptr());
550 vogl_message_printf("%s: Deserializing state snapshot \"%s\", %u bytes\n", VOGL_FUNCTION_NAME, id.get_ptr(), snapshot_data.size());
554 timed_scope ts("doc.binary_deserialize");
555 if (!doc.binary_deserialize(snapshot_data) || (!doc.get_root()))
557 vogl_error_printf("%s: Failed deserializing JSON snapshot blob data \"%s\"!\n", VOGL_FUNCTION_NAME, id.get_ptr());
562 pSnapshot = vogl_new(vogl_gl_state_snapshot);
564 timed_scope ts("pSnapshot->deserialize");
565 if (!pSnapshot->deserialize(*doc.get_root(), pTrace_reader->get_multi_blob_manager(), &trace_gl_ctypes))
567 vogl_delete(pSnapshot);
570 vogl_error_printf("%s: Failed deserializing snapshot blob data \"%s\"!\n", VOGL_FUNCTION_NAME, id.get_ptr());
574 found_snapshot = true;
584 } while (!found_snapshot);
589 //----------------------------------------------------------------------------------------------------------------------
590 // get_replayer_flags_from_command_line_params
591 //----------------------------------------------------------------------------------------------------------------------
592 static uint get_replayer_flags_from_command_line_params(bool interactive_mode)
594 uint replayer_flags = 0;
598 const char *m_pCommand;
600 } s_replayer_command_line_params[] =
602 { "benchmark", cGLReplayerBenchmarkMode },
603 { "verbose", cGLReplayerVerboseMode },
604 { "force_debug_context", cGLReplayerForceDebugContexts },
605 { "dump_all_packets", cGLReplayerDumpAllPackets },
606 { "debug", cGLReplayerDebugMode },
607 { "lock_window_dimensions", cGLReplayerLockWindowDimensions },
608 { "replay_debug", cGLReplayerLowLevelDebugMode },
609 { "dump_packet_blob_files_on_error", cGLReplayerDumpPacketBlobFilesOnError },
610 { "dump_shaders_on_draw", cGLReplayerDumpShadersOnDraw },
611 { "dump_packets_on_error", cGLReplayerDumpPacketsOnError },
612 { "dump_screenshots", cGLReplayerDumpScreenshots },
613 { "hash_backbuffer", cGLReplayerHashBackbuffer },
614 { "dump_backbuffer_hashes", cGLReplayerDumpBackbufferHashes },
615 { "sum_hashing", cGLReplayerSumHashing },
616 { "dump_framebuffer_on_draw", cGLReplayerDumpFramebufferOnDraws },
619 for (uint i = 0; i < sizeof(s_replayer_command_line_params) / sizeof(s_replayer_command_line_params[0]); i++)
620 if (g_command_line_params.get_value_as_bool(s_replayer_command_line_params[i].m_pCommand))
621 replayer_flags |= s_replayer_command_line_params[i].m_flag;
623 if (interactive_mode && !g_command_line_params.get_value_as_bool("disable_snapshot_caching"))
624 replayer_flags |= cGLReplayerSnapshotCaching;
626 return replayer_flags;
629 //----------------------------------------------------------------------------------------------------------------------
631 //----------------------------------------------------------------------------------------------------------------------
632 static bool tool_replay_mode()
636 dynamic_string trace_filename(g_command_line_params.get_value_as_string_or_empty("", 1));
637 if (trace_filename.is_empty())
639 vogl_error_printf("%s: No trace file specified!\n", VOGL_FUNCTION_NAME);
643 dynamic_string actual_trace_filename;
644 vogl_unique_ptr<vogl_trace_file_reader> pTrace_reader(vogl_open_trace_file(trace_filename, actual_trace_filename, g_command_line_params.get_value_as_string_or_empty("loose_file_path").get_ptr()));
645 if (!pTrace_reader.get())
647 vogl_error_printf("%s: File not found, or unable to determine file type of trace file \"%s\"\n", VOGL_FUNCTION_NAME, trace_filename.get_ptr());
651 vogl_printf("Reading trace file %s\n", actual_trace_filename.get_ptr());
653 bool interactive_mode = g_command_line_params.get_value_as_bool("interactive");
655 vogl_gl_replayer replayer;
657 uint replayer_flags = get_replayer_flags_from_command_line_params(interactive_mode);
659 vogl_replay_window window;
661 // TODO: This will create a window with default attributes, which seems fine for the majority of traces.
662 // Unfortunately, some GL call streams *don't* want an alpha channel, or depth, or stencil etc. in the default framebuffer so this may become a problem.
663 // Also, this design only supports a single window, which is going to be a problem with multiple window traces.
664 if (!window.open(g_command_line_params.get_value_as_int("width", 0, 1024, 1, 65535), g_command_line_params.get_value_as_int("height", 0, 768, 1, 65535)))
666 vogl_error_printf("%s: Failed initializing replay window\n", VOGL_FUNCTION_NAME);
670 if (!replayer.init(replayer_flags, &window, pTrace_reader->get_sof_packet(), pTrace_reader->get_multi_blob_manager()))
672 vogl_error_printf("%s: Failed initializing GL replayer\n", VOGL_FUNCTION_NAME);
676 if (replayer_flags & cGLReplayerBenchmarkMode)
678 // Also disable all glGetError() calls in vogl_utils.cpp.
679 vogl_disable_gl_get_error();
682 replayer.set_swap_sleep_time(g_command_line_params.get_value_as_uint("swap_sleep"));
683 replayer.set_dump_framebuffer_on_draw_prefix(g_command_line_params.get_value_as_string("dump_framebuffer_on_draw_prefix", 0, "screenshot"));
684 replayer.set_screenshot_prefix(g_command_line_params.get_value_as_string("dump_screenshots_prefix", 0, "screenshot"));
685 replayer.set_backbuffer_hash_filename(g_command_line_params.get_value_as_string_or_empty("dump_backbuffer_hashes"));
686 replayer.set_dump_framebuffer_on_draw_frame_index(g_command_line_params.get_value_as_int("dump_framebuffer_on_draw_frame", 0, -1, 0, INT_MAX));
687 replayer.set_dump_framebuffer_on_draw_first_gl_call_index(g_command_line_params.get_value_as_int("dump_framebuffer_on_draw_first_gl_call", 0, -1, 0, INT_MAX));
688 replayer.set_dump_framebuffer_on_draw_last_gl_call_index(g_command_line_params.get_value_as_int("dump_framebuffer_on_draw_last_gl_call", 0, -1, 0, INT_MAX));
690 XSelectInput(window.get_display(), window.get_xwindow(),
691 EnterWindowMask | LeaveWindowMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | ExposureMask | FocusChangeMask | KeyPressMask | KeyReleaseMask | PropertyChangeMask | StructureNotifyMask | KeymapStateMask);
693 Atom wmDeleteMessage = XInternAtom(window.get_display(), "WM_DELETE_WINDOW", False);
694 XSetWMProtocols(window.get_display(), window.get_xwindow(), &wmDeleteMessage, 1);
696 Bool win_mapped = false;
698 vogl_gl_state_snapshot *pSnapshot = NULL;
699 int64_t snapshot_loop_start_frame = -1;
700 int64_t snapshot_loop_end_frame = -1;
702 vogl::hash_map<uint64_t> keys_pressed, keys_down;
703 dynamic_string keyframe_base_filename(g_command_line_params.get_value_as_string("keyframe_base_filename"));
704 vogl::vector<uint64_t> keyframes;
705 int64_t paused_mode_frame_index = -1;
706 int64_t take_snapshot_at_frame_index = g_command_line_params.get_value_as_int64("pause_on_frame", 0, -1);
707 bool paused_mode = false;
708 bool slow_mode = false;
710 if (!keyframe_base_filename.is_empty())
713 if (!finder.find((keyframe_base_filename + "*.bin").get_ptr()))
715 vogl_error_printf("Failed finding files: %s\n", keyframe_base_filename.get_ptr());
719 for (uint i = 0; i < finder.get_files().size(); i++)
721 dynamic_string base_name(finder.get_files()[i].m_name);
722 dynamic_string ext(base_name);
723 file_utils::get_extension(ext);
727 file_utils::remove_extension(base_name);
728 int underscore_ofs = base_name.find_right('_');
729 if (underscore_ofs < 0)
732 dynamic_string frame_index_str(base_name.right(underscore_ofs + 1));
733 if (frame_index_str.is_empty())
736 const char *pFrame_index = frame_index_str.get_ptr();
738 uint64_t frame_index = 0;
739 if (!string_ptr_to_uint64(pFrame_index, frame_index))
742 keyframes.push_back(frame_index);
743 printf("Found keyframe file %s index %" PRIu64 "\n", finder.get_files()[i].m_fullname.get_ptr(), frame_index);
749 int loop_frame = g_command_line_params.get_value_as_int("loop_frame", 0, -1);
750 int loop_len = math::maximum<int>(g_command_line_params.get_value_as_int("loop_len", 0, 1), 1);
751 int loop_count = math::maximum<int>(g_command_line_params.get_value_as_int("loop_count", 0, cINT32_MAX), 1);
752 int draw_kill_max_thresh = g_command_line_params.get_value_as_int("draw_kill_max_thresh", 0, -1);
753 bool endless_mode = g_command_line_params.get_value_as_bool("endless");
755 bool multitrim_mode = g_command_line_params.get_value_as_bool("multitrim");
756 int multitrim_interval = g_command_line_params.get_value_as_int("multitrim_interval", 0, 1, 1);
757 int multitrim_frames_remaining = 0;
759 int64_t write_snapshot_index = g_command_line_params.get_value_as_int64("write_snapshot_call", 0, -1, 0);
760 dynamic_string write_snapshot_filename = g_command_line_params.get_value_as_string("write_snapshot_file", 0, "state_snapshot.json");
762 int64_t trim_call_index = g_command_line_params.get_value_as_int64("trim_call", 0, -1, 0);
763 vogl::vector<uint> trim_frames(g_command_line_params.get_count("trim_frame"));
764 for (uint i = 0; i < trim_frames.size(); i++)
766 bool parsed_successfully;
767 trim_frames[i] = g_command_line_params.get_value_as_uint("trim_frame", i, 0, 0, cUINT32_MAX, 0, &parsed_successfully);
768 if (!parsed_successfully)
770 vogl_error_printf("%s: Failed parsing -trim_frame at index %u\n", VOGL_FUNCTION_NAME, i);
775 vogl::vector<dynamic_string> trim_filenames(g_command_line_params.get_count("trim_file"));
776 for (uint i = 0; i < trim_filenames.size(); i++)
778 dynamic_string filename(g_command_line_params.get_value_as_string("trim_file", i));
780 if (filename.is_empty())
782 vogl_error_printf("%s: Invalid trim filename\n", VOGL_FUNCTION_NAME);
786 trim_filenames[i] = filename;
788 if (file_utils::add_default_extension(trim_filenames[i], ".bin"))
789 vogl_message_printf("%s: Trim output filename \"%s\", didn't have an extension, appended \".bin\" to the filename: %s\n", VOGL_FUNCTION_NAME, filename.get_ptr(), trim_filenames[i].get_ptr());
792 vogl::vector<uint> trim_lens(g_command_line_params.get_count("trim_len"));
793 for (uint i = 0; i < trim_lens.size(); i++)
795 bool parsed_successfully;
796 trim_lens[i] = g_command_line_params.get_value_as_uint("trim_len", i, 1, 0, cUINT32_MAX, 0, &parsed_successfully);
797 if (!parsed_successfully)
799 vogl_error_printf("%s: Failed parsing -trim_len at index %u\n", VOGL_FUNCTION_NAME, i);
804 uint num_trim_files_written = 0;
805 uint highest_frame_to_trim = 0;
807 vogl_loose_file_blob_manager trim_file_blob_manager;
811 if (trim_frames.size())
813 if (trim_filenames.is_empty())
815 console::error("%s: -trim_frame specified without specifying at least one -trim_file or -trim_call\n", VOGL_FUNCTION_NAME);
820 if (write_snapshot_index >= 0)
822 if ((multitrim_mode) || (trim_frames.size()) || (trim_lens.size()) || (trim_call_index >= 0))
824 console::warning("%s: Can't write snapshot and trim at the same time, disabling trimming\n", VOGL_FUNCTION_NAME);
826 multitrim_mode = false;
829 trim_call_index = -1;
832 if (draw_kill_max_thresh > 0)
834 console::warning("%s: Write snapshot mode is enabled, disabling -draw_kill_max_thresh\n", VOGL_FUNCTION_NAME);
835 draw_kill_max_thresh = -1;
840 console::warning("%s: Write snapshot mode is enabled, disabling -endless\n", VOGL_FUNCTION_NAME);
841 endless_mode = false;
844 else if ((trim_frames.size()) || (trim_call_index != -1))
846 if (trim_filenames.is_empty())
848 console::error("%s: Must also specify at least one -trim_file\n", VOGL_FUNCTION_NAME);
852 if (trim_call_index != -1)
854 if (trim_frames.size())
856 console::error("%s: Can't specify both -trim_call and -trim_frame\n", VOGL_FUNCTION_NAME);
862 console::error("%s: Can't specify both -trim_call and -multitrim\n", VOGL_FUNCTION_NAME);
866 if (trim_filenames.size() > 1)
868 console::warning("%s: -trim_call specified but more than 1 -trim_file specified - ignoring all but first -trim_file's\n", VOGL_FUNCTION_NAME);
869 trim_filenames.resize(1);
873 trim_file_blob_manager.init(cBMFWritable);
875 if (trim_frames.size() > 1)
877 if ((trim_filenames.size() > 1) && (trim_filenames.size() != trim_frames.size()))
879 console::error("%s: More than 1 -trim_frame was specified, and more than 1 -trim_file was specified, but the number of -trim_file's must match the number of -trim_frame's (or only specify one -trim_file to use it as a base filename)\n", VOGL_FUNCTION_NAME);
883 if ((trim_lens.size() > 1) && (trim_lens.size() != trim_frames.size()))
885 console::error("%s: More than 1 -trim_frame was specified, and more than 1 -trim_len's was specified, but the number of -trim_len's must match the number of -trim_frame's (or only specify one -trim_len to use it for all trims)\n", VOGL_FUNCTION_NAME);
890 if ((multitrim_mode) && (trim_filenames.size() > 1))
892 console::warning("%s: Only 1 filename needs to be specified in -multitrim mode\n", VOGL_FUNCTION_NAME);
895 if (loop_frame != -1)
897 console::warning("%s: Trim is enabled, disabling -loop_frame\n", VOGL_FUNCTION_NAME);
903 if (draw_kill_max_thresh > 0)
905 console::warning("%s: Trim is enabled, disabling -draw_kill_max_thresh\n", VOGL_FUNCTION_NAME);
906 draw_kill_max_thresh = -1;
911 console::warning("%s: Trim is enabled, disabling -endless\n", VOGL_FUNCTION_NAME);
912 endless_mode = false;
915 if (trim_call_index == -1)
917 for (uint tf = 0; tf < trim_frames.size(); tf++)
920 if (trim_lens.size())
922 if (trim_lens.size() < trim_frames.size())
927 len = math::maximum(len, 1U);
928 highest_frame_to_trim = math::maximum<uint>(highest_frame_to_trim, trim_frames[tf] + len - 1);
934 if (trim_filenames.size())
935 console::warning("%s: -trim_file was specified, but -trim_frame was not specified so ignoring\n", VOGL_FUNCTION_NAME);
936 if (trim_lens.size())
937 console::warning("%s: -trim_len was specified, but -trim_frame was not specified so ignoring\n", VOGL_FUNCTION_NAME);
939 trim_filenames.clear();
947 tmZone(TELEMETRY_LEVEL0, TMZF_NONE, "Main Loop");
949 while (X11_Pending(window.get_display()))
953 // Watch for new X eventsn
954 XNextEvent(window.get_display(), &newEvent);
956 switch (newEvent.type)
960 KeySym xsym = XLookupKeysym(&newEvent.xkey, 0);
962 //printf("KeyPress 0%04" PRIX64 "%" PRIu64 "\n", (uint64_t)xsym, (uint64_t)xsym);
964 keys_down.insert(xsym);
965 keys_pressed.insert(xsym);
971 KeySym xsym = XLookupKeysym(&newEvent.xkey, 0);
973 //printf("KeyRelease 0x%04" PRIX64 " %" PRIu64 "\n", (uint64_t)xsym, (uint64_t)xsym);
975 keys_down.erase(xsym);
982 //printf("FocusIn/FocusOut\n");
990 //XRefreshKeyboardMapping(&newEvent);
995 //printf("UnmapNotify\n");
1005 //printf("MapNotify\n");
1011 if (!replayer.update_window_dimensions())
1016 case ConfigureNotify:
1018 if (!replayer.update_window_dimensions())
1025 vogl_message_printf("Exiting\n");
1030 if (newEvent.xclient.data.l[0] == (int)wmDeleteMessage)
1032 vogl_message_printf("Exiting\n");
1043 // TODO: Massively refactor this! Move the replayer into a class, etc.
1044 if (interactive_mode)
1052 tmZone(TELEMETRY_LEVEL0, TMZF_NONE, "Interactive");
1054 // Interactive mode is more of a test bad to validate a bunch of classes. It's kind of narly because the replayer's object can be in odd intermediate/pending states during window
1055 // resizes - hopefully this complexity will go away once we get pbuffer's or whatever in.
1056 vogl_debug_printf("%s: At frame boundary: %u, beginning of frame %u, pause frame %" PRIi64 ", taking snapshot at frame %" PRIi64 "\n", VOGL_FUNCTION_NAME, replayer.get_at_frame_boundary(), replayer.get_frame_index(), paused_mode_frame_index, take_snapshot_at_frame_index);
1058 if (keys_pressed.contains('c'))
1060 keys_pressed.erase('c');
1061 if (replayer.is_valid())
1063 dynamic_string filename;
1064 for (uint i = 0; i < 10000000; i++)
1066 filename.format("screenshot_%06u.png", i);
1067 if (!file_utils::does_file_exist(filename.get_ptr()))
1071 replayer.dump_frontbuffer_screenshot_before_next_swap(filename);
1075 if (keys_pressed.contains('s'))
1077 keys_pressed.erase('s');
1078 slow_mode = !slow_mode;
1081 // When paused, we'll NOT be at a frame boundary because the prev. loop applied a state snapshot (which will be pending)
1082 if (replayer.get_at_frame_boundary() && !replayer.get_pending_apply_snapshot())
1084 bool take_new_snapshot = false;
1086 // See if we've scheduled a snapshot at this frame
1087 if ((int64_t)replayer.get_frame_index() == take_snapshot_at_frame_index)
1089 take_new_snapshot = true;
1091 take_snapshot_at_frame_index = -1;
1093 // Check for pausing
1094 else if (keys_pressed.contains(XK_space))
1096 keys_pressed.erase(XK_space);
1100 console::info("Unpausing\n");
1102 keys_pressed.erase(XK_space);
1104 vogl_delete(pSnapshot);
1107 paused_mode_frame_index = -1;
1109 paused_mode = false;
1113 console::info("Pausing\n");
1117 take_new_snapshot = true;
1121 // Snapshot the current state
1122 if (take_new_snapshot)
1124 vogl_delete(pSnapshot);
1127 pSnapshot = replayer.snapshot_state();
1130 vogl_error_printf("%s: Snapshot failed!\n", VOGL_FUNCTION_NAME);
1134 if (g_command_line_params.get_value_as_bool("debug_test_snapshot_serialization"))
1136 // Obviously, this crap is only for debugging.
1137 vogl_memory_blob_manager mem_blob_manager;
1138 mem_blob_manager.init(cBMFReadWrite);
1140 json_document temp_json_doc;
1141 if (!pSnapshot->serialize(*temp_json_doc.get_root(), mem_blob_manager, &replayer.get_trace_gl_ctypes()))
1143 console::error("%s: Failed serializing state snapshot!\n", VOGL_FUNCTION_NAME);
1148 temp_json_doc.binary_serialize(ubj_data);
1149 temp_json_doc.binary_deserialize(ubj_data);
1152 vogl_gl_state_snapshot *pNew_snapshot = vogl_new(vogl_gl_state_snapshot);
1154 if (!pNew_snapshot->deserialize(*temp_json_doc.get_root(), mem_blob_manager, &replayer.get_trace_gl_ctypes()))
1156 console::error("%s: Failed deserializing state snapshot!\n", VOGL_FUNCTION_NAME);
1160 vogl_delete(pSnapshot);
1162 pSnapshot = pNew_snapshot;
1167 paused_mode_frame_index = replayer.get_frame_index();
1173 // Begin processing the next frame
1174 bool applied_snapshot = false;
1175 vogl_gl_replayer::status_t status = replayer.process_pending_window_resize(&applied_snapshot);
1177 if (status == vogl_gl_replayer::cStatusOK)
1179 if (replayer.get_at_frame_boundary())
1181 const char *pWindow_name = (sizeof(void *) == sizeof(uint32)) ? "voglreplay 32-bit" : "voglreplay 64-bit";
1182 dynamic_string window_title(cVarArg, "%s: File: %s Frame %u %s %s", pWindow_name, trace_filename.get_ptr(), replayer.get_frame_index(), paused_mode ? "PAUSED" : "", keyframes.find_sorted(replayer.get_frame_index()) >= 0 ? "(At Keyframe)" : "");
1183 window.set_title(window_title.get_ptr());
1186 // At this point, if we're paused the frame snapshot as been applied, and we're just about going to replay the frame's commands.
1187 if (((applied_snapshot) || (replayer.get_at_frame_boundary())) &&
1188 (keys_pressed.contains('t') || keys_pressed.contains('j')))
1190 uint64_t frame_to_trim;
1192 frame_to_trim = paused_mode_frame_index;
1194 frame_to_trim = replayer.get_frame_index();
1196 dynamic_string trim_name;
1197 for (uint i = 0; i < 1000000; i++)
1199 trim_name.format("trim_%06" PRIu64 "_%u", frame_to_trim, i);
1200 if (!file_utils::does_dir_exist(trim_name.get_ptr()))
1204 if (!file_utils::create_directory(trim_name.get_ptr()))
1206 vogl_error_printf("%s: Failed creating trim directory %s\n", VOGL_FUNCTION_NAME, trim_name.get_ptr());
1210 vogl_loose_file_blob_manager trim_file_blob_manager;
1211 trim_file_blob_manager.init(cBMFReadWrite, trim_name.get_ptr());
1213 dynamic_string trim_filename(trim_name + "/" + trim_name + ".bin");
1214 dynamic_string snapshot_id;
1215 uint write_trim_file_flags = vogl_gl_replayer::cWriteTrimFileFromStartOfFrame | (g_command_line_params.get_value_as_bool("no_trim_optimization") ? 0 : vogl_gl_replayer::cWriteTrimFileOptimizeSnapshot);
1216 if (replayer.write_trim_file(write_trim_file_flags, trim_filename, 1, *pTrace_reader, &snapshot_id))
1218 dynamic_string json_trim_base_filename(trim_name + "/j" + trim_name);
1220 char voglreplay_exec_filename[1024];
1221 file_utils::get_exec_filename(voglreplay_exec_filename, sizeof(voglreplay_exec_filename));
1223 dynamic_string convert_to_json_spawn_str(cVarArg, "%s --dump \"%s\" \"%s\"", voglreplay_exec_filename, trim_filename.get_ptr(), json_trim_base_filename.get_ptr());
1224 if (system(convert_to_json_spawn_str.get_ptr()) != 0)
1226 vogl_error_printf("%s: Failed running voglreplay: %s\n", VOGL_FUNCTION_NAME, convert_to_json_spawn_str.get_ptr());
1230 dynamic_string json_trim_full_filename(trim_name + "/j" + trim_name + "_000000.json");
1232 dynamic_string view_json_spawn_str(cVarArg, "np \"%s\" &", json_trim_full_filename.get_ptr());
1233 system(view_json_spawn_str.get_ptr());
1236 if (keys_pressed.contains('j'))
1238 dynamic_string workdir(".");
1239 file_utils::full_path(workdir);
1241 dynamic_string replay_json_spawn_str(cVarArg, "konsole --workdir \"%s\" --hold -e \"%s\" --endless \"%s\" &", workdir.get_ptr(), voglreplay_exec_filename, json_trim_base_filename.get_ptr());
1242 system(replay_json_spawn_str.get_ptr());
1247 keys_pressed.erase('t');
1248 keys_pressed.erase('j');
1251 // Now replay the next frame's GL commands up to the swap
1252 status = replayer.process_frame(*pTrace_reader);
1255 if (status == vogl_gl_replayer::cStatusHardFailure)
1258 if ((slow_mode) && (!paused_mode))
1261 int64_t seek_to_target_frame = -1;
1262 bool seek_to_closest_keyframe = false;
1263 int seek_to_closest_frame_dir_bias = 0;
1265 if (status == vogl_gl_replayer::cStatusAtEOF)
1267 vogl_message_printf("%s: At trace EOF, frame index %u\n", VOGL_FUNCTION_NAME, replayer.get_frame_index());
1269 // Right after the last swap in the file, either rewind or pause back on the last frame
1270 if ((paused_mode) && (replayer.get_frame_index()))
1272 seek_to_target_frame = replayer.get_frame_index() - 1;
1273 take_snapshot_at_frame_index = -1;
1277 vogl_printf("Resetting state and rewinding back to frame 0\n");
1279 replayer.reset_state();
1281 if (!pTrace_reader->seek_to_frame(0))
1283 vogl_error_printf("%s: Failed rewinding trace reader!\n", VOGL_FUNCTION_NAME);
1287 take_snapshot_at_frame_index = -1;
1288 paused_mode_frame_index = -1;
1289 paused_mode = false;
1292 vogl_delete(pSnapshot);
1295 else if (replayer.get_at_frame_boundary() && (!replayer.get_pending_apply_snapshot()))
1297 // Rewind to beginning
1298 if (keys_pressed.contains('r'))
1300 bool ctrl = (keys_down.contains(XK_Control_L) || keys_down.contains(XK_Control_R));
1301 keys_pressed.erase('r');
1303 vogl_delete(pSnapshot);
1306 if ((paused_mode) && (ctrl))
1307 seek_to_target_frame = paused_mode_frame_index;
1309 take_snapshot_at_frame_index = -1;
1310 paused_mode_frame_index = -1;
1311 paused_mode = false;
1313 vogl_printf("Resetting state and rewinding back to frame 0\n");
1315 replayer.reset_state();
1317 if (!pTrace_reader->seek_to_frame(0))
1319 vogl_error_printf("%s: Failed rewinding trace reader!\n", VOGL_FUNCTION_NAME);
1323 // Seek to last frame
1324 else if (keys_pressed.contains('e'))
1326 keys_pressed.erase('e');
1330 vogl_delete(pSnapshot);
1333 paused_mode_frame_index = -1;
1334 take_snapshot_at_frame_index = -1;
1336 vogl_printf("Seeking to last frame\n");
1338 int64_t max_frame_index = pTrace_reader->get_max_frame_index();
1339 if (max_frame_index < 0)
1341 vogl_error_printf("%s: Failed determining the total number of trace frames!\n", VOGL_FUNCTION_NAME);
1345 // "frames" are actually "swaps" to the tracer/replayer, so don't seek to the "last" swap (because no rendering will follow that one), but the one before
1346 seek_to_target_frame = max_frame_index - 1;
1349 // Check if paused and process seek related keypresses
1350 else if ((paused_mode) && (pSnapshot))
1353 for (num_key = '0'; num_key <= '9'; num_key++)
1355 if (keys_pressed.contains(num_key))
1357 keys_pressed.erase(num_key);
1364 int64_t max_frame_index = pTrace_reader->get_max_frame_index();
1365 if (max_frame_index < 0)
1367 vogl_error_printf("%s: Failed determining the total number of trace frames!\n", VOGL_FUNCTION_NAME);
1371 float fraction = ((num_key - '0') + 1) / 11.0f;
1373 seek_to_target_frame = math::clamp<int64_t>(static_cast<int64_t>(max_frame_index * fraction + .5f), 0, max_frame_index - 1);
1374 seek_to_closest_keyframe = true;
1376 else if (keys_pressed.contains(XK_Left) || keys_pressed.contains(XK_Right))
1378 int dir = keys_pressed.contains(XK_Left) ? -1 : 1;
1380 bool shift = (keys_down.contains(XK_Shift_L) || keys_down.contains(XK_Shift_R));
1381 bool ctrl = (keys_down.contains(XK_Control_L) || keys_down.contains(XK_Control_R));
1384 if ((shift) && (ctrl))
1391 int rel = dir * mag;
1393 int64_t target_frame_index = math::maximum<int64_t>(0, paused_mode_frame_index + rel);
1395 seek_to_target_frame = target_frame_index;
1397 keys_pressed.erase(XK_Left);
1398 keys_pressed.erase(XK_Right);
1400 if ((keyframes.size()) && (keys_down.contains(XK_Alt_L) || keys_down.contains(XK_Alt_R)))
1402 uint keyframe_array_index = 0;
1403 for (keyframe_array_index = 1; keyframe_array_index < keyframes.size(); keyframe_array_index++)
1404 if ((int64_t)keyframes[keyframe_array_index] > paused_mode_frame_index)
1409 if ((paused_mode_frame_index == static_cast<int64_t>(keyframes[keyframe_array_index - 1])) && (keyframe_array_index > 1))
1410 keyframe_array_index = keyframe_array_index - 2;
1412 keyframe_array_index = keyframe_array_index - 1;
1416 if (keyframe_array_index < keyframes.size())
1418 if ((paused_mode_frame_index == static_cast<int64_t>(keyframes[keyframe_array_index])) && ((keyframe_array_index + 1) < keyframes.size()))
1419 keyframe_array_index = keyframe_array_index + 1;
1421 // keyframe_array_index = keyframe_array_index;
1424 keyframe_array_index = keyframe_array_index - 1;
1427 seek_to_target_frame = keyframes[keyframe_array_index];
1431 if (((dir < 0) && (target_frame_index < seek_to_target_frame)) || (target_frame_index > seek_to_target_frame))
1432 seek_to_target_frame = target_frame_index;
1434 seek_to_closest_keyframe = true;
1435 seek_to_closest_frame_dir_bias = dir;
1438 console::info("Seeking to keyframe array index %u, target frame %" PRIu64 "\n", keyframe_array_index, seek_to_target_frame);
1441 // Check for unpause
1442 else if (keys_pressed.contains(XK_space))
1444 console::info("Unpausing\n");
1446 keys_pressed.erase(XK_space);
1448 vogl_delete(pSnapshot);
1451 paused_mode_frame_index = -1;
1453 paused_mode = false;
1457 // We're paused so seek back to the command right after the swap we're paused on, apply the paused frame's snapshot, then play the frame over
1458 status = replayer.begin_applying_snapshot(pSnapshot, false);
1459 if ((status != vogl_gl_replayer::cStatusOK) && (status != vogl_gl_replayer::cStatusResizeWindow))
1461 vogl_error_printf("%s: Failed applying snapshot!\n", VOGL_FUNCTION_NAME);
1465 pTrace_reader->seek_to_frame(static_cast<uint>(paused_mode_frame_index));
1470 // Seek to target frame
1471 if (seek_to_target_frame != -1)
1473 vogl_delete(pSnapshot);
1475 paused_mode_frame_index = -1;
1477 if ((int64_t)replayer.get_frame_index() == seek_to_target_frame)
1478 take_snapshot_at_frame_index = seek_to_target_frame;
1481 uint keyframe_array_index = 0;
1482 for (keyframe_array_index = 1; keyframe_array_index < keyframes.size(); keyframe_array_index++)
1483 if (static_cast<int64_t>(keyframes[keyframe_array_index]) > seek_to_target_frame)
1486 if ((!keyframes.is_empty()) && (static_cast<int64_t>(keyframes[keyframe_array_index - 1]) <= seek_to_target_frame))
1488 int keyframe_array_index_to_use = keyframe_array_index - 1;
1490 if (seek_to_closest_keyframe)
1492 if (!seek_to_closest_frame_dir_bias)
1494 if ((keyframe_array_index_to_use + 1U) < keyframes.size())
1496 if (llabs(static_cast<int64_t>(keyframes[keyframe_array_index_to_use + 1]) - seek_to_target_frame) < llabs(static_cast<int64_t>(keyframes[keyframe_array_index_to_use]) - seek_to_target_frame))
1498 keyframe_array_index_to_use++;
1502 else if (seek_to_closest_frame_dir_bias > 0)
1504 if ((keyframe_array_index_to_use + 1U) < keyframes.size())
1506 if (static_cast<int64_t>(keyframes[keyframe_array_index_to_use]) <= seek_to_target_frame)
1508 keyframe_array_index_to_use++;
1514 int64_t keyframe_index = keyframes[keyframe_array_index_to_use];
1516 if (seek_to_closest_keyframe)
1517 seek_to_target_frame = keyframe_index;
1519 vogl_debug_printf("Seeking to target frame %" PRIu64 "\n", seek_to_target_frame);
1521 dynamic_string keyframe_filename(cVarArg, "%s_%06" PRIu64 ".bin", keyframe_base_filename.get_ptr(), keyframe_index);
1523 vogl_gl_state_snapshot *pKeyframe_snapshot = read_state_snapshot_from_trace(keyframe_filename);
1524 if (!pKeyframe_snapshot)
1527 bool delete_snapshot_after_applying = true;
1528 if (seek_to_target_frame == keyframe_index)
1529 delete_snapshot_after_applying = false;
1531 status = replayer.begin_applying_snapshot(pKeyframe_snapshot, delete_snapshot_after_applying);
1532 if ((status != vogl_gl_replayer::cStatusOK) && (status != vogl_gl_replayer::cStatusResizeWindow))
1534 vogl_error_printf("%s: Failed applying snapshot!\n", VOGL_FUNCTION_NAME);
1538 pKeyframe_snapshot->set_frame_index(static_cast<uint>(keyframe_index));
1540 if (!pTrace_reader->seek_to_frame(static_cast<uint>(keyframe_index)))
1542 vogl_error_printf("%s: Failed seeking to keyframe!\n", VOGL_FUNCTION_NAME);
1546 if (seek_to_target_frame == keyframe_index)
1548 pSnapshot = pKeyframe_snapshot;
1549 paused_mode_frame_index = seek_to_target_frame;
1552 take_snapshot_at_frame_index = seek_to_target_frame;
1556 if (seek_to_target_frame < static_cast<int64_t>(replayer.get_frame_index()))
1558 replayer.reset_state();
1559 pTrace_reader->seek_to_frame(0);
1562 take_snapshot_at_frame_index = seek_to_target_frame;
1567 else // !interactive_mode
1569 tmZone(TELEMETRY_LEVEL0, TMZF_NONE, "!Interactive");
1571 if (replayer.get_at_frame_boundary())
1573 if (trim_frames.size())
1575 bool should_trim = false;
1579 for (tf = 0; tf < trim_frames.size(); tf++)
1581 if (trim_lens.size())
1583 if (trim_lens.size() < trim_frames.size())
1586 len = trim_lens[tf];
1588 len = math::maximum(len, 1U);
1592 if ((replayer.get_frame_index() >= trim_frames[tf]) && (replayer.get_frame_index() < (trim_frames[tf] + math::maximum(len, 1U))))
1600 if (replayer.get_frame_index() == trim_frames[tf])
1612 if (multitrim_frames_remaining)
1614 should_trim = false;
1618 multitrim_frames_remaining = multitrim_interval;
1621 multitrim_frames_remaining--;
1625 multitrim_frames_remaining = 0;
1628 //printf("multitrim_interval: %u %u\n", multitrim_frames_remaining, multitrim_interval);
1632 dynamic_string filename;
1634 if ((multitrim_mode) || (trim_filenames.size() < trim_frames.size()))
1636 filename = trim_filenames[0];
1638 if ((multitrim_mode) || (trim_frames.size() > 1))
1640 dynamic_string drive, dir, fname, ext;
1641 file_utils::split_path(filename.get_ptr(), &drive, &dir, &fname, &ext);
1643 dynamic_string new_fname(cVarArg, "%s_%06u", fname.get_ptr(), replayer.get_frame_index());
1645 file_utils::combine_path_and_extension(filename, &drive, &dir, &new_fname, &ext);
1650 filename = trim_filenames[tf];
1653 dynamic_string trim_path(file_utils::get_pathname(filename.get_ptr()));
1654 trim_file_blob_manager.set_path(trim_path);
1656 file_utils::create_directories(trim_path, false);
1658 uint write_trim_file_flags = vogl_gl_replayer::cWriteTrimFileFromStartOfFrame | (g_command_line_params.get_value_as_bool("no_trim_optimization") ? 0 : vogl_gl_replayer::cWriteTrimFileOptimizeSnapshot);
1659 if (!replayer.write_trim_file(write_trim_file_flags, filename, multitrim_mode ? 1 : len, *pTrace_reader))
1662 num_trim_files_written++;
1664 if (!multitrim_mode)
1666 if (num_trim_files_written == trim_frames.size())
1668 vogl_message_printf("%s: All requested trim files written, stopping replay\n", VOGL_FUNCTION_NAME);
1676 uint64_t next_frame_index = replayer.get_frame_index() + 1;
1678 if (next_frame_index > highest_frame_to_trim)
1680 vogl_message_printf("%s: No more frames to trim, stopping replay\n", VOGL_FUNCTION_NAME);
1686 if ((!pSnapshot) && (loop_frame != -1) && (static_cast<int64_t>(replayer.get_frame_index()) == loop_frame))
1688 vogl_debug_printf("%s: Capturing replayer state at start of frame %u\n", VOGL_FUNCTION_NAME, replayer.get_frame_index());
1690 pSnapshot = replayer.snapshot_state();
1694 vogl_printf("Snapshot succeeded\n");
1696 snapshot_loop_start_frame = pTrace_reader->get_cur_frame();
1697 snapshot_loop_end_frame = pTrace_reader->get_cur_frame() + loop_len;
1699 if (draw_kill_max_thresh > 0)
1701 replayer.set_frame_draw_counter_kill_threshold(0);
1704 vogl_debug_printf("%s: Loop start: %" PRIi64 " Loop end: %" PRIi64 "\n", VOGL_FUNCTION_NAME, snapshot_loop_start_frame, snapshot_loop_end_frame);
1708 vogl_error_printf("Snapshot failed!\n");
1715 vogl_gl_replayer::status_t status = replayer.process_pending_window_resize();
1716 if (status == vogl_gl_replayer::cStatusOK)
1720 status = replayer.process_next_packet(*pTrace_reader);
1722 if ((status != vogl_gl_replayer::cStatusHardFailure) && (status != vogl_gl_replayer::cStatusAtEOF))
1724 if ((write_snapshot_index >= 0) && (write_snapshot_index == replayer.get_last_processed_call_counter()))
1726 dynamic_string filename(write_snapshot_filename);
1728 dynamic_string write_snapshot_path(file_utils::get_pathname(filename.get_ptr()));
1729 trim_file_blob_manager.init(cBMFReadWrite, write_snapshot_path.get_ptr());
1731 file_utils::create_directories(write_snapshot_path, false);
1733 pSnapshot = replayer.snapshot_state();
1737 vogl_printf("Snapshot succeeded at call counter %" PRIu64 "\n", replayer.get_last_processed_call_counter());
1739 vogl_null_blob_manager null_blob_manager;
1740 null_blob_manager.init(cBMFReadWrite);
1743 vogl_blob_manager *pBlob_manager = g_command_line_params.get_value_as_bool("write_snapshot_blobs") ? static_cast<vogl_blob_manager *>(&trim_file_blob_manager) : static_cast<vogl_blob_manager *>(&null_blob_manager);
1744 if (!pSnapshot->serialize(*doc.get_root(), *pBlob_manager, &replayer.get_trace_gl_ctypes()))
1746 vogl_error_printf("Failed serializing state snapshot document!\n");
1748 else if (!doc.serialize_to_file(filename.get_ptr(), true))
1750 vogl_error_printf("Failed writing state snapshot to file \"%s\"!\n", filename.get_ptr());
1754 vogl_printf("Successfully wrote JSON snapshot to file \"%s\"\n", filename.get_ptr());
1757 vogl_delete(pSnapshot);
1762 vogl_error_printf("Snapshot failed!\n");
1767 else if ((trim_call_index >= 0) && (trim_call_index == replayer.get_last_processed_call_counter()))
1769 dynamic_string filename(trim_filenames[0]);
1771 dynamic_string trim_path(file_utils::get_pathname(filename.get_ptr()));
1772 trim_file_blob_manager.set_path(trim_path);
1774 file_utils::create_directories(trim_path, false);
1776 if (!replayer.write_trim_file(0, filename, trim_lens.size() ? trim_lens[0] : 1, *pTrace_reader, NULL))
1779 vogl_message_printf("%s: Trim file written, stopping replay\n", VOGL_FUNCTION_NAME);
1784 if ((status == vogl_gl_replayer::cStatusNextFrame) || (status == vogl_gl_replayer::cStatusResizeWindow) || (status == vogl_gl_replayer::cStatusAtEOF) || (status == vogl_gl_replayer::cStatusHardFailure))
1789 if (status == vogl_gl_replayer::cStatusHardFailure)
1792 if (status == vogl_gl_replayer::cStatusAtEOF)
1794 vogl_message_printf("%s: At trace EOF, frame index %u\n", VOGL_FUNCTION_NAME, replayer.get_frame_index());
1797 if ((replayer.get_at_frame_boundary()) && (pSnapshot) && (loop_count > 0) && ((pTrace_reader->get_cur_frame() == snapshot_loop_end_frame) || (status == vogl_gl_replayer::cStatusAtEOF)))
1799 status = replayer.begin_applying_snapshot(pSnapshot, false);
1800 if ((status != vogl_gl_replayer::cStatusOK) && (status != vogl_gl_replayer::cStatusResizeWindow))
1803 pTrace_reader->seek_to_frame(static_cast<uint>(snapshot_loop_start_frame));
1805 if (draw_kill_max_thresh > 0)
1807 int64_t thresh = replayer.get_frame_draw_counter_kill_threshold();
1809 if (thresh >= draw_kill_max_thresh)
1811 replayer.set_frame_draw_counter_kill_threshold(thresh);
1813 vogl_debug_printf("%s: Applying snapshot and seeking back to frame %" PRIi64 " draw kill thresh %" PRIu64 "\n", VOGL_FUNCTION_NAME, snapshot_loop_start_frame, thresh);
1816 vogl_debug_printf("%s: Applying snapshot and seeking back to frame %" PRIi64 "\n", VOGL_FUNCTION_NAME, snapshot_loop_start_frame);
1822 bool print_progress = (status == vogl_gl_replayer::cStatusAtEOF) || ((replayer.get_at_frame_boundary()) && ((replayer.get_frame_index() % 100) == 0));
1825 if (pTrace_reader->get_type() == cBINARY_TRACE_FILE_READER)
1827 vogl_binary_trace_file_reader &binary_trace_reader = *static_cast<vogl_binary_trace_file_reader *>(pTrace_reader.get());
1829 vogl_printf("Replay now at frame index %d, trace file offet %" PRIu64 ", GL call counter %" PRIu64 ", %3.2f%% percent complete\n",
1830 replayer.get_frame_index(),
1831 binary_trace_reader.get_cur_file_ofs(),
1832 replayer.get_last_parsed_call_counter(),
1833 binary_trace_reader.get_trace_file_size() ? (binary_trace_reader.get_cur_file_ofs() * 100.0f) / binary_trace_reader.get_trace_file_size() : 0);
1837 if (status == vogl_gl_replayer::cStatusAtEOF)
1841 double time_since_start = tm.get_elapsed_secs();
1843 vogl_printf("%u total swaps, %.3f secs, %3.3f avg fps\n", replayer.get_total_swaps(), time_since_start, replayer.get_frame_index() / time_since_start);
1848 vogl_printf("Resetting state and rewinding back to frame 0\n");
1850 replayer.reset_state();
1852 if (!pTrace_reader->seek_to_frame(0))
1854 vogl_error_printf("%s: Failed rewinding trace reader!\n", VOGL_FUNCTION_NAME);
1859 } // interactive_mode
1864 if (trim_frames.size())
1866 console::message("Wrote %u trim file(s)\n", num_trim_files_written);
1868 if (((multitrim_mode) && (!num_trim_files_written)) ||
1869 ((!multitrim_mode) && (num_trim_files_written != trim_frames.size())))
1870 console::warning("Requested %u trim frames, but was only able to write %u trim frames (one or more -trim_frames must have been too large)\n", trim_frames.size(), num_trim_files_written);
1875 if (g_command_line_params.get_value_as_bool("pause_on_exit") && (window.is_opened()))
1877 vogl_printf("Press a key to continue.\n");
1884 bool exit_flag = false;
1885 while (!exit_flag && X11_Pending(window.get_display()))
1888 XNextEvent(window.get_display(), &newEvent);
1890 switch (newEvent.type)
1900 if (newEvent.xclient.data.l[0] == (int)wmDeleteMessage)
1920 //----------------------------------------------------------------------------------------------------------------------
1922 //----------------------------------------------------------------------------------------------------------------------
1923 static bool tool_dump_mode()
1927 dynamic_string input_trace_filename(g_command_line_params.get_value_as_string_or_empty("", 1));
1928 if (input_trace_filename.is_empty())
1930 vogl_error_printf("Must specify filename of input binary trace file!\n");
1934 dynamic_string output_base_filename(g_command_line_params.get_value_as_string_or_empty("", 2));
1935 if (output_base_filename.is_empty())
1937 vogl_error_printf("Must specify base filename of output JSON/blob files!\n");
1941 vogl_loose_file_blob_manager output_file_blob_manager;
1943 dynamic_string output_trace_path(file_utils::get_pathname(output_base_filename.get_ptr()));
1944 vogl_debug_printf("%s: Output trace path: %s\n", VOGL_FUNCTION_NAME, output_trace_path.get_ptr());
1945 output_file_blob_manager.init(cBMFReadWrite, output_trace_path.get_ptr());
1947 file_utils::create_directories(output_trace_path, false);
1949 dynamic_string actual_input_trace_filename;
1950 vogl_unique_ptr<vogl_trace_file_reader> pTrace_reader(vogl_open_trace_file(input_trace_filename, actual_input_trace_filename, g_command_line_params.get_value_as_string_or_empty("loose_file_path").get_ptr()));
1951 if (!pTrace_reader.get())
1953 vogl_error_printf("%s: Failed opening input trace file \"%s\"\n", VOGL_FUNCTION_NAME, input_trace_filename.get_ptr());
1957 const bool full_verification = g_command_line_params.get_value_as_bool("verify");
1959 vogl_ctypes trace_gl_ctypes;
1960 trace_gl_ctypes.init(pTrace_reader->get_sof_packet().m_pointer_sizes);
1962 dynamic_string archive_name;
1963 if (pTrace_reader->get_archive_blob_manager().is_initialized())
1965 dynamic_string archive_filename(output_base_filename.get_ptr());
1966 archive_filename += "_trace_archive.zip";
1968 archive_name = file_utils::get_filename(archive_filename.get_ptr());
1970 vogl_message_printf("Writing trace archive \"%s\", size %" PRIu64 " bytes\n", archive_filename.get_ptr(), pTrace_reader->get_archive_blob_manager().get_archive_size());
1972 cfile_stream archive_stream;
1973 if (!archive_stream.open(archive_filename.get_ptr(), cDataStreamWritable | cDataStreamSeekable))
1975 vogl_error_printf("%s: Failed opening output trace archive \"%s\"!\n", VOGL_FUNCTION_NAME, archive_filename.get_ptr());
1979 if (!pTrace_reader->get_archive_blob_manager().write_archive_to_stream(archive_stream))
1981 vogl_error_printf("%s: Failed writing to output trace archive \"%s\"!\n", VOGL_FUNCTION_NAME, archive_filename.get_ptr());
1985 if (!archive_stream.close())
1987 vogl_error_printf("%s: Failed writing to output trace archive \"%s\"!\n", VOGL_FUNCTION_NAME, archive_filename.get_ptr());
1992 vogl_trace_packet gl_packet_cracker(&trace_gl_ctypes);
1994 uint cur_file_index = 0;
1995 uint cur_frame_index = 0;
1996 uint64_t cur_packet_index = 0;
1997 VOGL_NOTE_UNUSED(cur_packet_index);
1998 json_document cur_doc;
2001 json_node &meta_node = cur_doc.get_root()->add_object("meta");
2002 meta_node.add_key_value("cur_frame", cur_frame_index);
2004 json_node &sof_node = cur_doc.get_root()->add_object("sof");
2005 sof_node.add_key_value("pointer_sizes", pTrace_reader->get_sof_packet().m_pointer_sizes);
2006 sof_node.add_key_value("version", to_hex_string(pTrace_reader->get_sof_packet().m_version));
2007 if (!archive_name.is_empty())
2008 sof_node.add_key_value("archive_filename", archive_name);
2010 json_node &uuid_array = sof_node.add_array("uuid");
2011 for (uint i = 0; i < VOGL_ARRAY_SIZE(pTrace_reader->get_sof_packet().m_uuid); i++)
2012 uuid_array.add_value(pTrace_reader->get_sof_packet().m_uuid[i]);
2015 // TODO: Automatically dump binary snapshot file to text?
2016 // Right now we can't afford to do it at trace time, it takes too much memory.
2018 json_node *pPacket_array = &cur_doc.get_root()->add_array("packets");
2020 bool flush_current_document = false;
2026 uint64_t cur_packet_ofs = (pTrace_reader->get_type() == cBINARY_TRACE_FILE_READER) ? static_cast<vogl_binary_trace_file_reader *>(pTrace_reader.get())->get_cur_file_ofs() : 0;
2028 vogl_trace_file_reader::trace_file_reader_status_t read_status = pTrace_reader->read_next_packet();
2030 if (read_status == vogl_trace_file_reader::cEOF)
2032 vogl_message_printf("At trace file EOF\n");
2035 else if (read_status != vogl_trace_file_reader::cOK)
2037 vogl_error_printf("Failed reading from trace file, or file size was too small\n");
2043 if (pTrace_reader->get_packet_type() != cTSPTGLEntrypoint)
2045 if (pTrace_reader->get_packet_type() == cTSPTSOF)
2047 vogl_error_printf("Encountered redundant SOF packet!\n");
2051 json_node *pMeta_node = cur_doc.get_root()->find_child_object("meta");
2053 pMeta_node->add_key_value("eof", (pTrace_reader->get_packet_type() == cTSPTEOF) ? 1 : 2);
2058 if (flush_current_document)
2060 flush_current_document = false;
2062 dynamic_string output_filename(cVarArg, "%s_%06u.json", output_base_filename.get_ptr(), cur_file_index);
2063 vogl_message_printf("Writing file: \"%s\"\n", output_filename.get_ptr());
2065 if (!cur_doc.serialize_to_file(output_filename.get_ptr(), true))
2067 vogl_error_printf("%s: Failed serializing JSON document to file %s\n", VOGL_FUNCTION_NAME, output_filename.get_ptr());
2073 if (cur_doc.get_root()->check_for_duplicate_keys())
2074 vogl_warning_printf("%s: JSON document %s has nodes with duplicate keys, this document may not be readable by some JSON parsers\n", VOGL_FUNCTION_NAME, output_filename.get_ptr());
2078 pPacket_array = NULL;
2081 json_node ¬e_node = cur_doc.get_root()->add_object("meta");
2082 note_node.add_key_value("cur_frame", cur_frame_index);
2084 json_node &uuid_array = note_node.add_array("uuid");
2085 for (uint i = 0; i < VOGL_ARRAY_SIZE(pTrace_reader->get_sof_packet().m_uuid); i++)
2086 uuid_array.add_value(pTrace_reader->get_sof_packet().m_uuid[i]);
2088 pPacket_array = &cur_doc.get_root()->add_array("packets");
2091 const vogl_trace_gl_entrypoint_packet &gl_packet = pTrace_reader->get_packet<vogl_trace_gl_entrypoint_packet>();
2092 const char *pFunc_name = g_vogl_entrypoint_descs[gl_packet.m_entrypoint_id].m_pName;
2093 VOGL_NOTE_UNUSED(pFunc_name);
2095 if (g_command_line_params.get_value_as_bool("debug"))
2097 vogl_debug_printf("Trace packet: File offset: %" PRIu64 ", Total size %u, Param size: %u, Client mem size %u, Name value size %u, call %" PRIu64 ", ID: %s (%u), Thread ID: 0x%" PRIX64 ", Trace Context: 0x%" PRIX64 "\n",
2100 gl_packet.m_param_size,
2101 gl_packet.m_client_memory_size,
2102 gl_packet.m_name_value_map_size,
2103 gl_packet.m_call_counter,
2104 g_vogl_entrypoint_descs[gl_packet.m_entrypoint_id].m_pName,
2105 gl_packet.m_entrypoint_id,
2106 gl_packet.m_thread_id,
2107 gl_packet.m_context_handle);
2110 if (!gl_packet_cracker.deserialize(pTrace_reader->get_packet_buf().get_ptr(), pTrace_reader->get_packet_buf().size(), true))
2112 vogl_error_printf("Failed deserializing GL entrypoint packet. Trying to continue parsing the file, this may die!\n");
2119 json_node &new_node = pPacket_array->add_object();
2121 vogl_trace_packet::json_serialize_params serialize_params;
2122 serialize_params.m_output_basename = file_utils::get_filename(output_base_filename.get_ptr());
2123 serialize_params.m_pBlob_manager = &output_file_blob_manager;
2124 serialize_params.m_cur_frame = cur_file_index;
2125 serialize_params.m_write_debug_info = g_command_line_params.get_value_as_bool("write_debug_info");
2126 if (!gl_packet_cracker.json_serialize(new_node, serialize_params))
2128 vogl_error_printf("JSON serialization failed!\n");
2134 if (full_verification)
2137 if (!strcmp(pFunc_name, "glClearColor"))
2143 vogl::vector<char> new_node_as_text;
2144 new_node.serialize(new_node_as_text, true, 0);
2147 if (new_node_as_text.size())
2149 printf("%s\n", new_node_as_text.get_ptr());
2153 json_document round_tripped_node;
2154 if (!round_tripped_node.deserialize(new_node_as_text.get_ptr()) || !round_tripped_node.get_root())
2156 vogl_error_printf("Failed verifying serialized JSON data (step 1)!\n");
2163 vogl_trace_packet temp_cracker(&trace_gl_ctypes);
2164 bool success = temp_cracker.json_deserialize(*round_tripped_node.get_root(), "<memory>", &output_file_blob_manager);
2167 vogl_error_printf("Failed verifying serialized JSON data (step 2)!\n");
2174 success = gl_packet_cracker.compare(temp_cracker, false);
2177 vogl_error_printf("Failed verifying serialized JSON data (step 3)!\n");
2184 dynamic_stream dyn_stream;
2186 success = temp_cracker.serialize(dyn_stream);
2189 vogl_error_printf("Failed verifying serialized JSON data (step 4)!\n");
2196 vogl_trace_packet temp_cracker2(&trace_gl_ctypes);
2197 success = temp_cracker2.deserialize(static_cast<const uint8 *>(dyn_stream.get_ptr()), static_cast<uint32>(dyn_stream.get_size()), true);
2200 vogl_error_printf("Failed verifying serialized JSON data (step 5)!\n");
2205 success = gl_packet_cracker.compare(temp_cracker2, true);
2208 vogl_error_printf("Failed verifying serialized JSON data (step 6)!\n");
2215 uint64_t binary_serialized_size = dyn_stream.get_size();
2216 if (binary_serialized_size != pTrace_reader->get_packet_buf().size())
2218 vogl_error_printf("Round-tripped binary serialized size differs from original packet's' size (step 7)!\n");
2226 // This is excessive- the key value map fields may be binary serialized in different orders
2227 // TODO: maybe fix the key value map class so it serializes in a stable order (independent of hash table construction)?
2228 const uint8 *p = static_cast<const uint8 *>(dyn_stream.get_ptr());
2229 const uint8 *q = static_cast<const uint8 *>(pTrace_reader->get_packet_buf().get_ptr());
2230 if (memcmp(p, q, binary_serialized_size) != 0)
2232 file_utils::write_buf_to_file("p.bin", p, binary_serialized_size);
2233 file_utils::write_buf_to_file("q.bin", q, binary_serialized_size);
2234 vogl_error_printf("Round-tripped binary serialized data differs from original packet's data (step 7)!\n");
2248 if (vogl_is_swap_buffers_entrypoint(static_cast<gl_entrypoint_id_t>(gl_packet.m_entrypoint_id)))
2250 flush_current_document = true;
2254 if (cur_doc.get_root()->size() >= 1 * 1000 * 1000)
2256 // TODO: Support replaying dumps like this, or fix the code to serialize the text as it goes.
2257 vogl_error_printf("Haven't encountered a SwapBuffers() call in over 1000000 GL calls, dumping current in-memory JSON document to disk to avoid running out of memory. This JSON dump may not be replayable, but writing it anyway.\n");
2258 flush_current_document = true;
2262 json_node *pMeta_node = cur_doc.get_root()->find_child_object("meta");
2266 if (!pMeta_node->has_key("eof"))
2267 pMeta_node->add_key_value("eof", 2);
2269 dynamic_string output_filename(cVarArg, "%s_%06u.json", output_base_filename.get_ptr(), cur_file_index);
2270 vogl_message_printf("Writing file: \"%s\"\n", output_filename.get_ptr());
2272 if (!cur_doc.serialize_to_file(output_filename.get_ptr(), true))
2274 vogl_error_printf("%s: Failed serializing JSON document to file %s\n", VOGL_FUNCTION_NAME, output_filename.get_ptr());
2279 if (cur_doc.get_root()->check_for_duplicate_keys())
2280 vogl_warning_printf("%s: JSON document %s has nodes with duplicate keys, this document may not be readable by some JSON parsers\n", VOGL_FUNCTION_NAME, output_filename.get_ptr());
2286 vogl_error_printf("Failed dumping binary trace to JSON files starting with filename prefix \"%s\" (but wrote as much as possible)\n", output_base_filename.get_ptr());
2288 vogl_message_printf("Successfully dumped binary trace to %u JSON file(s) starting with filename prefix \"%s\"\n", cur_file_index, output_base_filename.get_ptr());
2293 //----------------------------------------------------------------------------------------------------------------------
2295 //----------------------------------------------------------------------------------------------------------------------
2296 static bool tool_parse_mode()
2300 dynamic_string input_base_filename(g_command_line_params.get_value_as_string_or_empty("", 1));
2301 if (input_base_filename.is_empty())
2303 vogl_error_printf("Must specify filename of input JSON/blob trace files!\n");
2307 dynamic_string actual_input_filename;
2308 vogl_unique_ptr<vogl_trace_file_reader> pTrace_reader(vogl_open_trace_file(input_base_filename, actual_input_filename, g_command_line_params.get_value_as_string_or_empty("loose_file_path").get_ptr()));
2309 if (!pTrace_reader.get())
2312 dynamic_string output_trace_filename(g_command_line_params.get_value_as_string_or_empty("", 2));
2313 if (output_trace_filename.is_empty())
2315 vogl_error_printf("Must specify full filename of output binary trace file!\n");
2319 file_utils::create_directories(output_trace_filename, true);
2321 if (file_utils::add_default_extension(output_trace_filename, ".bin"))
2322 vogl_message_printf("Trim output filename doesn't have an extension, appending \".bin\" to the filename\n");
2324 // TODO: Refactor this, I think we're going to write the actual ctypes and entrpoint descs, etc. into the trace archive next.
2325 vogl_ctypes trace_ctypes;
2326 trace_ctypes.init(pTrace_reader->get_sof_packet().m_pointer_sizes);
2328 vogl_trace_file_writer trace_writer(&trace_ctypes);
2329 if (!trace_writer.open(output_trace_filename.get_ptr(), NULL, true, false, pTrace_reader->get_sof_packet().m_pointer_sizes))
2331 vogl_error_printf("Unable to create file \"%s\"!\n", output_trace_filename.get_ptr());
2335 if (pTrace_reader->get_archive_blob_manager().is_initialized())
2337 dynamic_string_array blob_files(pTrace_reader->get_archive_blob_manager().enumerate());
2338 for (uint i = 0; i < blob_files.size(); i++)
2340 if (blob_files[i] == VOGL_TRACE_ARCHIVE_FRAME_FILE_OFFSETS_FILENAME)
2343 vogl_message_printf("Adding blob file %s to output trace archive\n", blob_files[i].get_ptr());
2345 uint8_vec blob_data;
2346 if (pTrace_reader->get_archive_blob_manager().get(blob_files[i], blob_data))
2348 if (trace_writer.get_trace_archive()->add_buf_using_id(blob_data.get_ptr(), blob_data.size(), blob_files[i]).is_empty())
2350 vogl_error_printf("%s: Failed writing blob data %s to output trace archive!\n", VOGL_FUNCTION_NAME, blob_files[i].get_ptr());
2356 vogl_error_printf("%s: Failed reading blob data %s from trace archive!\n", VOGL_FUNCTION_NAME, blob_files[i].get_ptr());
2364 vogl_trace_file_reader::trace_file_reader_status_t read_status = pTrace_reader->read_next_packet();
2366 if ((read_status != vogl_trace_file_reader::cOK) && (read_status != vogl_trace_file_reader::cEOF))
2368 vogl_error_printf("Failed reading from trace file\n");
2372 if ((read_status == vogl_trace_file_reader::cEOF) || (pTrace_reader->is_eof_packet()))
2374 vogl_message_printf("At trace file EOF\n");
2378 const vogl::vector<uint8> &packet_buf = pTrace_reader->get_packet_buf();
2380 if (!trace_writer.write_packet(packet_buf.get_ptr(), packet_buf.size(), pTrace_reader->is_swap_buffers_packet()))
2382 vogl_error_printf("Failed writing to output trace file \"%s\"\n", output_trace_filename.get_ptr());
2387 if (!trace_writer.close())
2389 vogl_error_printf("Failed closing output trace file \"%s\"\n", output_trace_filename.get_ptr());
2393 vogl_message_printf("Successfully wrote binary trace file \"%s\"\n", output_trace_filename.get_ptr());
2398 trace_writer.close();
2400 vogl_warning_printf("Processing failed, output trace file \"%s\" may be invalid!\n", output_trace_filename.get_ptr());
2405 //----------------------------------------------------------------------------------------------------------------------
2406 // struct histo_entry
2407 //----------------------------------------------------------------------------------------------------------------------
2408 typedef vogl::map<dynamic_string> dynamic_string_set;
2409 typedef vogl::map<dynamic_string, uint64_t> dynamic_string_hash_map;
2410 typedef vogl::map<uint> uint_map;
2417 histo_entry(dynamic_string_hash_map::const_iterator it, double val)
2418 : m_it(it), m_value(val)
2422 dynamic_string_hash_map::const_iterator m_it;
2425 bool operator<(const histo_entry &rhs) const
2427 return m_value > rhs.m_value;
2431 //----------------------------------------------------------------------------------------------------------------------
2433 //----------------------------------------------------------------------------------------------------------------------
2434 #define SAFE_FLOAT_DIV(n, d) (d ? ((double)(n) / (d)) : 0)
2436 static void dump_histogram(const char *pMsg, const dynamic_string_hash_map &map, uint64_t total_gl_entrypoint_packets, uint64_t total_swaps)
2440 vogl::vector<histo_entry> histo;
2441 for (dynamic_string_hash_map::const_iterator it = map.begin(); it != map.end(); ++it)
2442 histo.push_back(histo_entry(it, static_cast<double>(it->second)));
2444 vogl_printf("\n----------------------\n%s: %u\n", pMsg, map.size());
2446 for (uint i = 0; i < histo.size(); i++)
2448 dynamic_string_hash_map::const_iterator it = histo[i].m_it;
2449 vogl_printf("%s: Total calls: %" PRIu64 " %3.1f%%, Avg calls per frame: %f\n", it->first.get_ptr(), it->second, SAFE_FLOAT_DIV(it->second * 100.0f, total_gl_entrypoint_packets), SAFE_FLOAT_DIV(it->second, total_swaps));
2453 //----------------------------------------------------------------------------------------------------------------------
2455 //----------------------------------------------------------------------------------------------------------------------
2456 static bool tool_info_mode()
2460 dynamic_string input_base_filename(g_command_line_params.get_value_as_string_or_empty("", 1));
2461 if (input_base_filename.is_empty())
2463 vogl_error_printf("Must specify filename of input JSON/blob trace files!\n");
2467 dynamic_string actual_input_filename;
2468 vogl_unique_ptr<vogl_trace_file_reader> pTrace_reader(vogl_open_trace_file(input_base_filename, actual_input_filename, g_command_line_params.get_value_as_string_or_empty("loose_file_path").get_ptr()));
2469 if (!pTrace_reader.get())
2472 vogl_printf("Scanning trace file %s\n", actual_input_filename.get_ptr());
2474 const vogl_trace_stream_start_of_file_packet &sof_packet = pTrace_reader->get_sof_packet();
2476 if (pTrace_reader->get_type() == cBINARY_TRACE_FILE_READER)
2478 uint64_t file_size = dynamic_cast<vogl_binary_trace_file_reader *>(pTrace_reader.get())->get_stream().get_size();
2479 vogl_printf("Total file size: %s\n", uint64_to_string_with_commas(file_size).get_ptr());
2482 vogl_printf("SOF packet size: %" PRIu64 " bytes\n", sof_packet.m_size);
2483 vogl_printf("Version: 0x%04X\n", sof_packet.m_version);
2484 vogl_printf("UUID: 0x%08x 0x%08x 0x%08x 0x%08x\n", sof_packet.m_uuid[0], sof_packet.m_uuid[1], sof_packet.m_uuid[2], sof_packet.m_uuid[3]);
2485 vogl_printf("First packet offset: %" PRIu64 "\n", sof_packet.m_first_packet_offset);
2486 vogl_printf("Trace pointer size: %u\n", sof_packet.m_pointer_sizes);
2487 vogl_printf("Trace archive size: %" PRIu64 " offset: %" PRIu64 "\n", sof_packet.m_archive_size, sof_packet.m_archive_offset);
2488 vogl_printf("Can quickly seek forward: %u\nMax frame index: %" PRIu64 "\n", pTrace_reader->can_quickly_seek_forward(), pTrace_reader->get_max_frame_index());
2490 if (!pTrace_reader->get_archive_blob_manager().is_initialized())
2492 vogl_warning_printf("This trace does not have a trace archive!\n");
2496 vogl_printf("----------------------\n");
2497 vogl::vector<dynamic_string> archive_files(pTrace_reader->get_archive_blob_manager().enumerate());
2498 vogl_printf("Total trace archive files: %u\n", archive_files.size());
2499 for (uint i = 0; i < archive_files.size(); i++)
2500 vogl_printf("\"%s\"\n", archive_files[i].get_ptr());
2501 vogl_printf("----------------------\n");
2504 uint min_packet_size = cUINT32_MAX;
2505 uint max_packet_size = 0;
2506 uint64_t total_packets = 1; // 1, not 0, to account for the SOF packet
2507 uint64_t total_packet_bytes = 0;
2508 uint64_t total_swaps = 0;
2509 uint64_t total_make_currents = 0;
2510 uint64_t total_internal_trace_commands = 0;
2511 uint64_t total_non_gl_entrypoint_packets = 1; // 1, not 0, to account for the SOF packet
2512 uint64_t total_gl_entrypoint_packets = 0;
2513 uint64_t total_gl_commands = 0;
2514 uint64_t total_glx_commands = 0;
2515 uint64_t total_wgl_commands = 0;
2516 uint64_t total_unknown_commands = 0;
2517 uint64_t total_draws = 0;
2518 bool found_eof_packet = false;
2520 uint64_t min_packets_per_frame = cUINT64_MAX;
2521 uint64_t max_packets_per_frame = 0;
2522 uint64_t max_frame_make_currents = 0;
2523 uint64_t min_frame_make_currents = cUINT64_MAX;
2525 uint64_t min_frame_draws = cUINT64_MAX;
2526 uint64_t max_frame_draws = 0;
2527 uint64_t cur_frame_draws = 0;
2529 uint64_t cur_frame_packet_count = 0;
2530 uint64_t total_frame_make_currents = 0;
2532 uint64_t num_non_whitelisted_funcs = 0;
2533 dynamic_string_set non_whitelisted_funcs_called;
2535 uint64_t total_programs_linked = 0;
2536 uint64_t total_program_binary_calls = 0;
2537 uint_map unique_programs_used;
2539 uint total_gl_state_snapshots = 0;
2541 uint total_display_list_calls = false;
2542 uint total_gl_get_errors = 0;
2544 uint total_context_creates = 0;
2545 uint total_context_destroys = 0;
2547 dynamic_string_hash_map all_apis_called, category_histogram, version_histogram, profile_histogram, deprecated_histogram;
2549 vogl_ctypes trace_gl_ctypes(pTrace_reader->get_sof_packet().m_pointer_sizes);
2551 vogl_trace_packet trace_packet(&trace_gl_ctypes);
2555 vogl_trace_file_reader::trace_file_reader_status_t read_status = pTrace_reader->read_next_packet();
2557 if ((read_status != vogl_trace_file_reader::cOK) && (read_status != vogl_trace_file_reader::cEOF))
2559 vogl_error_printf("Failed reading from trace file!\n");
2564 if (read_status == vogl_trace_file_reader::cEOF)
2566 vogl_printf("At trace file EOF on swap %" PRIu64 "\n", total_swaps);
2570 const vogl::vector<uint8> &packet_buf = pTrace_reader->get_packet_buf();
2571 VOGL_NOTE_UNUSED(packet_buf);
2573 uint packet_size = pTrace_reader->get_packet_size();
2575 min_packet_size = math::minimum<uint>(min_packet_size, packet_size);
2576 max_packet_size = math::maximum<uint>(max_packet_size, packet_size);
2578 total_packet_bytes += packet_size;
2580 cur_frame_packet_count++;
2582 const vogl_trace_stream_packet_base &base_packet = pTrace_reader->get_base_packet();
2583 VOGL_NOTE_UNUSED(base_packet);
2584 const vogl_trace_gl_entrypoint_packet *pGL_packet = NULL;
2586 if (pTrace_reader->get_packet_type() == cTSPTGLEntrypoint)
2588 if (!trace_packet.deserialize(pTrace_reader->get_packet_buf().get_ptr(), pTrace_reader->get_packet_buf().size(), false))
2590 console::error("%s: Failed parsing GL entrypoint packet\n", VOGL_FUNCTION_NAME);
2594 pGL_packet = &pTrace_reader->get_packet<vogl_trace_gl_entrypoint_packet>();
2595 gl_entrypoint_id_t entrypoint_id = static_cast<gl_entrypoint_id_t>(pGL_packet->m_entrypoint_id);
2597 const gl_entrypoint_desc_t &entrypoint_desc = g_vogl_entrypoint_descs[entrypoint_id];
2599 all_apis_called[entrypoint_desc.m_pName]++;
2600 category_histogram[entrypoint_desc.m_pCategory]++;
2601 version_histogram[entrypoint_desc.m_pVersion]++;
2602 profile_histogram[entrypoint_desc.m_pProfile]++;
2603 deprecated_histogram[entrypoint_desc.m_pDeprecated]++;
2605 if (!entrypoint_desc.m_is_whitelisted)
2607 num_non_whitelisted_funcs++;
2608 non_whitelisted_funcs_called.insert(entrypoint_desc.m_pName);
2611 if (!strcmp(entrypoint_desc.m_pAPI_prefix, "GLX"))
2612 total_glx_commands++;
2613 else if (!strcmp(entrypoint_desc.m_pAPI_prefix, "WGL"))
2614 total_wgl_commands++;
2615 else if (!strcmp(entrypoint_desc.m_pAPI_prefix, "GL"))
2616 total_gl_commands++;
2618 total_unknown_commands++;
2620 total_gl_entrypoint_packets++;
2622 if (vogl_is_swap_buffers_entrypoint(entrypoint_id))
2625 if ((total_swaps & 255) == 255)
2626 console::progress("Frame %" PRIu64 "\n", total_swaps);
2628 min_packets_per_frame = math::minimum(min_packets_per_frame, cur_frame_packet_count);
2629 max_packets_per_frame = math::maximum(max_packets_per_frame, cur_frame_packet_count);
2631 min_frame_draws = math::minimum(min_frame_draws, cur_frame_draws);
2632 max_frame_draws = math::maximum(max_frame_draws, cur_frame_draws);
2634 max_frame_make_currents = math::maximum(max_frame_make_currents, total_frame_make_currents);
2635 min_frame_make_currents = math::minimum(min_frame_make_currents, total_frame_make_currents);
2637 cur_frame_packet_count = 0;
2638 total_frame_make_currents = 0;
2639 cur_frame_draws = 0;
2641 else if (vogl_is_draw_entrypoint(entrypoint_id))
2646 else if (vogl_is_make_current_entrypoint(entrypoint_id))
2648 total_make_currents++;
2649 total_frame_make_currents++;
2652 switch (entrypoint_id)
2654 case VOGL_ENTRYPOINT_glInternalTraceCommandRAD:
2656 total_internal_trace_commands++;
2658 GLuint cmd = trace_packet.get_param_value<GLuint>(0);
2659 GLuint size = trace_packet.get_param_value<GLuint>(1);
2660 VOGL_NOTE_UNUSED(size);
2662 if (cmd == cITCRKeyValueMap)
2664 key_value_map &kvm = trace_packet.get_key_value_map();
2666 dynamic_string cmd_type(kvm.get_string("command_type"));
2667 if (cmd_type == "state_snapshot")
2669 total_gl_state_snapshots++;
2675 case VOGL_ENTRYPOINT_glProgramBinary:
2677 total_program_binary_calls++;
2680 case VOGL_ENTRYPOINT_glLinkProgram:
2681 case VOGL_ENTRYPOINT_glLinkProgramARB:
2683 total_programs_linked++;
2686 case VOGL_ENTRYPOINT_glUseProgram:
2687 case VOGL_ENTRYPOINT_glUseProgramObjectARB:
2689 GLuint trace_handle = trace_packet.get_param_value<GLuint>(0);
2690 unique_programs_used.insert(trace_handle);
2694 case VOGL_ENTRYPOINT_glGenLists:
2695 case VOGL_ENTRYPOINT_glDeleteLists:
2696 case VOGL_ENTRYPOINT_glXUseXFont:
2697 case VOGL_ENTRYPOINT_glCallList:
2698 case VOGL_ENTRYPOINT_glCallLists:
2699 case VOGL_ENTRYPOINT_glListBase:
2701 total_display_list_calls++;
2704 case VOGL_ENTRYPOINT_glGetError:
2706 total_gl_get_errors++;
2709 case VOGL_ENTRYPOINT_glXCreateContext:
2710 case VOGL_ENTRYPOINT_glXCreateContextAttribsARB:
2712 total_context_creates++;
2715 case VOGL_ENTRYPOINT_glXDestroyContext:
2717 total_context_destroys++;
2726 total_non_gl_entrypoint_packets++;
2729 if (pTrace_reader->get_packet_type() == cTSPTEOF)
2731 found_eof_packet = true;
2732 vogl_printf("Found trace file EOF packet on swap %" PRIu64 "\n", total_swaps);
2740 #define PRINT_UINT_VAR(x) vogl_printf("%s: %u\n", dynamic_string(#x).replace("_", " ").get_ptr(), x);
2741 #define PRINT_UINT64_VAR(x) vogl_printf("%s: %" PRIu64 "\n", dynamic_string(#x).replace("_", " ").get_ptr(), x);
2742 #define PRINT_FLOAT(x, f) vogl_printf("%s: %f\n", dynamic_string(#x).replace("_", " ").get_ptr(), f);
2744 PRINT_UINT64_VAR(num_non_whitelisted_funcs);
2745 PRINT_UINT_VAR(total_gl_state_snapshots);
2747 PRINT_UINT64_VAR(total_swaps);
2749 PRINT_UINT64_VAR(total_make_currents);
2750 PRINT_FLOAT(avg_make_currents_per_frame, SAFE_FLOAT_DIV(total_make_currents, total_swaps));
2751 PRINT_UINT64_VAR(max_frame_make_currents);
2752 PRINT_UINT64_VAR(min_frame_make_currents);
2754 PRINT_UINT64_VAR(total_draws);
2755 PRINT_FLOAT(avg_draws_per_frame, SAFE_FLOAT_DIV(total_draws, total_swaps));
2756 PRINT_UINT64_VAR(min_frame_draws);
2757 PRINT_UINT64_VAR(max_frame_draws);
2759 PRINT_UINT_VAR(min_packet_size);
2760 PRINT_UINT_VAR(max_packet_size);
2761 PRINT_UINT64_VAR(total_packets);
2762 PRINT_UINT64_VAR(total_packet_bytes);
2763 PRINT_FLOAT(avg_packet_bytes_per_frame, SAFE_FLOAT_DIV(total_packet_bytes, total_swaps));
2764 PRINT_FLOAT(avg_packet_size, SAFE_FLOAT_DIV(total_packet_bytes, total_packets));
2765 PRINT_UINT64_VAR(min_packets_per_frame);
2766 PRINT_UINT64_VAR(max_packets_per_frame);
2767 PRINT_FLOAT(avg_packets_per_frame, SAFE_FLOAT_DIV(total_packets, total_swaps));
2769 PRINT_UINT64_VAR(total_internal_trace_commands);
2770 PRINT_UINT64_VAR(total_non_gl_entrypoint_packets);
2771 PRINT_UINT64_VAR(total_gl_entrypoint_packets);
2772 PRINT_UINT64_VAR(total_gl_commands);
2773 PRINT_UINT64_VAR(total_glx_commands);
2774 PRINT_UINT64_VAR(total_wgl_commands);
2775 PRINT_UINT64_VAR(total_unknown_commands);
2777 PRINT_UINT_VAR(found_eof_packet);
2779 PRINT_UINT_VAR(total_display_list_calls);
2780 printf("Avg display lists calls per frame: %f\n", SAFE_FLOAT_DIV(total_display_list_calls, total_swaps));
2782 PRINT_UINT_VAR(total_gl_get_errors);
2783 printf("Avg glGetError calls per frame: %f\n", SAFE_FLOAT_DIV(total_gl_get_errors, total_swaps));
2785 PRINT_UINT_VAR(total_context_creates);
2786 PRINT_UINT_VAR(total_context_destroys);
2789 #undef PRINT_UINT64_VAR
2792 vogl_printf("----------------------\n%s: %" PRIu64 "\n", "Total calls to glLinkProgram/glLinkProgramARB", total_programs_linked);
2793 vogl_printf("%s: %" PRIu64 "\n", "Total calls to glProgramBinary", total_program_binary_calls);
2794 vogl_printf("%s: %u\n", "Total unique program handles passed to glUseProgram/glUseProgramObjectARB", unique_programs_used.size());
2796 dump_histogram("API histogram", all_apis_called, total_gl_entrypoint_packets, total_swaps);
2797 dump_histogram("API Category histogram", category_histogram, total_gl_entrypoint_packets, total_swaps);
2798 dump_histogram("API Version histogram", version_histogram, total_gl_entrypoint_packets, total_swaps);
2799 //dump_histogram("API Profile histogram", profile_histogram, total_gl_entrypoint_packets, total_swaps);
2800 //dump_histogram("API Deprecated histogram", deprecated_histogram, total_gl_entrypoint_packets, total_swaps);
2802 #undef DUMP_HISTOGRAM
2804 if (non_whitelisted_funcs_called.size())
2806 vogl_warning_printf("\n----------------------\n");
2807 vogl_warning_printf("Number of non-whitelisted functions actually called: %u\n", non_whitelisted_funcs_called.size());
2808 for (dynamic_string_set::const_iterator it = non_whitelisted_funcs_called.begin(); it != non_whitelisted_funcs_called.end(); ++it)
2809 vogl_warning_printf("%s\n", it->first.get_ptr());
2810 vogl_warning_printf("\n----------------------\n");
2815 #undef SAFE_FLOAT_DIV
2817 //----------------------------------------------------------------------------------------------------------------------
2818 // param_value_matches
2819 //----------------------------------------------------------------------------------------------------------------------
2820 static bool param_value_matches(const bigint128 &value_to_find, vogl_namespace_t find_namespace, uint64_t value_data, vogl_ctype_t value_ctype, vogl_namespace_t value_namespace)
2822 if (find_namespace != VOGL_NAMESPACE_UNKNOWN)
2824 if (find_namespace != value_namespace)
2830 switch (value_ctype)
2833 case VOGL_GLBOOLEAN:
2847 // Not supporting float/double finds for now
2853 // Not supporting float/double finds for now
2862 value = static_cast<int32>(value_data);
2867 value = static_cast<int16>(value_data);
2872 value = static_cast<int8>(value_data);
2876 case VOGL_GLINT64EXT:
2878 value = static_cast<int64_t>(value_data);
2888 if (value == value_to_find)
2894 //----------------------------------------------------------------------------------------------------------------------
2896 //----------------------------------------------------------------------------------------------------------------------
2897 static void print_match(const vogl_trace_packet &trace_packet, int param_index, int array_element_index, uint64_t total_swaps)
2900 vogl_trace_packet::json_serialize_params params;
2901 trace_packet.json_serialize(*doc.get_root(), params);
2903 dynamic_string packet_as_json;
2904 doc.serialize(packet_as_json);
2906 if (param_index == -2)
2907 vogl_printf("----- Function match, frame %" PRIu64 ":\n", total_swaps);
2908 else if (param_index < 0)
2909 vogl_printf("----- Return value match, frame %" PRIu64 ":\n", total_swaps);
2910 else if (array_element_index >= 0)
2911 vogl_printf("----- Parameter %i element %i match, frame %" PRIu64 ":\n", param_index, array_element_index, total_swaps);
2913 vogl_printf("----- Parameter %i match, frame %" PRIu64 ":\n", param_index, total_swaps);
2915 vogl_printf("%s\n", packet_as_json.get_ptr());
2918 //----------------------------------------------------------------------------------------------------------------------
2920 //----------------------------------------------------------------------------------------------------------------------
2921 static bool tool_find_mode()
2925 dynamic_string input_base_filename(g_command_line_params.get_value_as_string_or_empty("", 1));
2926 if (input_base_filename.is_empty())
2928 vogl_error_printf("Must specify filename of input JSON/blob trace files!\n");
2932 dynamic_string actual_input_filename;
2933 vogl_unique_ptr<vogl_trace_file_reader> pTrace_reader(vogl_open_trace_file(input_base_filename, actual_input_filename, g_command_line_params.get_value_as_string_or_empty("loose_file_path").get_ptr()));
2934 if (!pTrace_reader.get())
2937 bigint128 value_to_find(static_cast<uint64_t>(gl_enums::cUnknownEnum));
2938 bool has_find_param = g_command_line_params.has_key("find_param");
2941 dynamic_string find_value_str(g_command_line_params.get_value_as_string("find_param"));
2942 if ((find_value_str.has_content()) && (vogl_isalpha(find_value_str[0])))
2944 value_to_find = g_gl_enums.find_enum(find_value_str);
2947 if (value_to_find == static_cast<uint64_t>(gl_enums::cUnknownEnum))
2949 bool find_param_u64_valid = false;
2950 value_to_find = g_command_line_params.get_value_as_uint64("find_param", 0, 0, 0, cUINT64_MAX, 0, &find_param_u64_valid);
2952 if (!find_param_u64_valid)
2954 bool find_param_i64_valid = false;
2955 value_to_find = g_command_line_params.get_value_as_int64("find_param", 0, 0, 0, cINT64_MAX, 0, &find_param_i64_valid);
2957 if (!find_param_i64_valid)
2959 vogl_error_printf("Failed parsing \"-find_param X\" command line option!\n");
2966 dynamic_string find_namespace_str(g_command_line_params.get_value_as_string_or_empty("find_namespace"));
2967 vogl_namespace_t find_namespace = VOGL_NAMESPACE_UNKNOWN;
2968 if (find_namespace_str.has_content())
2970 find_namespace = vogl_find_namespace_from_gl_type(find_namespace_str.get_ptr());
2971 if ((find_namespace == VOGL_NAMESPACE_INVALID) && (find_namespace_str != "invalid"))
2973 vogl_error_printf("Invalid namespace: \"%s\"\n", find_namespace_str.get_ptr());
2978 dynamic_string find_param_name(g_command_line_params.get_value_as_string_or_empty("find_param_name"));
2980 dynamic_string find_func_pattern(g_command_line_params.get_value_as_string_or_empty("find_func"));
2982 if (find_func_pattern.has_content())
2984 if (!func_regex.init(find_func_pattern.get_ptr(), REGEX_IGNORE_CASE))
2986 vogl_error_printf("Invalid func regex: \"%s\"\n", find_func_pattern.get_ptr());
2991 int64_t find_frame_low = g_command_line_params.get_value_as_int64("find_frame_low", 0, -1);
2992 int64_t find_frame_high = g_command_line_params.get_value_as_int64("find_frame_high", 0, -1);
2993 int64_t find_call_low = g_command_line_params.get_value_as_int64("find_call_low", 0, -1);
2994 int64_t find_call_high = g_command_line_params.get_value_as_int64("find_call_high", 0, -1);
2996 vogl_printf("Scanning trace file %s\n", actual_input_filename.get_ptr());
2998 vogl_ctypes trace_gl_ctypes(pTrace_reader->get_sof_packet().m_pointer_sizes);
2999 vogl_trace_packet trace_packet(&trace_gl_ctypes);
3001 uint64_t total_matches = 0;
3002 uint64_t total_swaps = 0;
3006 vogl_trace_file_reader::trace_file_reader_status_t read_status = pTrace_reader->read_next_packet();
3008 if ((read_status != vogl_trace_file_reader::cOK) && (read_status != vogl_trace_file_reader::cEOF))
3010 vogl_error_printf("Failed reading from trace file!\n");
3014 if (read_status == vogl_trace_file_reader::cEOF)
3017 const vogl::vector<uint8> &packet_buf = pTrace_reader->get_packet_buf();
3018 VOGL_NOTE_UNUSED(packet_buf);
3020 const vogl_trace_stream_packet_base &base_packet = pTrace_reader->get_base_packet();
3021 VOGL_NOTE_UNUSED(base_packet);
3023 const vogl_trace_gl_entrypoint_packet *pGL_packet = NULL;
3024 VOGL_NOTE_UNUSED(pGL_packet);
3026 if (pTrace_reader->get_packet_type() == cTSPTEOF)
3028 else if (pTrace_reader->get_packet_type() != cTSPTGLEntrypoint)
3031 if (!trace_packet.deserialize(pTrace_reader->get_packet_buf().get_ptr(), pTrace_reader->get_packet_buf().size(), false))
3033 console::error("%s: Failed parsing GL entrypoint packet\n", VOGL_FUNCTION_NAME);
3037 pGL_packet = &pTrace_reader->get_packet<vogl_trace_gl_entrypoint_packet>();
3039 if (find_frame_low >= 0)
3041 if (total_swaps < static_cast<uint64_t>(find_frame_low))
3044 if (find_frame_high >= 0)
3046 if (total_swaps > static_cast<uint64_t>(find_frame_high))
3050 if (find_call_low >= 0)
3052 if (trace_packet.get_call_counter() < static_cast<uint64_t>(find_call_low))
3055 if (find_call_high >= 0)
3057 if (trace_packet.get_call_counter() > static_cast<uint64_t>(find_call_high))
3061 if (func_regex.is_initialized())
3063 if (!func_regex.full_match(trace_packet.get_entrypoint_desc().m_pName))
3067 if (!has_find_param)
3069 print_match(trace_packet, -2, -1, total_swaps);
3074 if (trace_packet.has_return_value())
3076 if ((find_param_name.is_empty()) || (find_param_name == "return"))
3078 if (param_value_matches(value_to_find, find_namespace, trace_packet.get_return_value_data(), trace_packet.get_return_value_ctype(), trace_packet.get_return_value_namespace()))
3080 print_match(trace_packet, -1, -1, total_swaps);
3086 for (uint i = 0; i < trace_packet.total_params(); i++)
3088 if ((find_param_name.has_content()) && (find_param_name != trace_packet.get_param_desc(i).m_pName))
3091 const vogl_ctype_desc_t ¶m_ctype_desc = trace_packet.get_param_ctype_desc(i);
3093 if (param_ctype_desc.m_is_pointer)
3095 if ((!param_ctype_desc.m_is_opaque_pointer) && (param_ctype_desc.m_pointee_ctype != VOGL_VOID) && (trace_packet.has_param_client_memory(i)))
3097 const vogl_client_memory_array array(trace_packet.get_param_client_memory_array(i));
3099 for (uint j = 0; j < array.size(); j++)
3101 if (param_value_matches(value_to_find, find_namespace, array.get_element<uint64_t>(j), array.get_element_ctype(), trace_packet.get_param_namespace(i)))
3103 print_match(trace_packet, i, j, total_swaps);
3109 else if (param_value_matches(value_to_find, find_namespace, trace_packet.get_param_data(i), trace_packet.get_param_ctype(i), trace_packet.get_param_namespace(i)))
3111 print_match(trace_packet, i, -1, total_swaps);
3118 if (vogl_is_swap_buffers_entrypoint(trace_packet.get_entrypoint_id()))
3123 vogl_printf("Total matches found: %" PRIu64 "\n", total_matches);
3128 //----------------------------------------------------------------------------------------------------------------------
3129 // tool_compare_hash_files
3130 //----------------------------------------------------------------------------------------------------------------------
3131 static bool tool_compare_hash_files()
3133 dynamic_string src1_filename(g_command_line_params.get_value_as_string_or_empty("", 1));
3134 dynamic_string src2_filename(g_command_line_params.get_value_as_string_or_empty("", 2));
3135 if ((src1_filename.is_empty()) || (src2_filename.is_empty()))
3137 vogl_error_printf("Must specify two source filenames!\n");
3141 dynamic_string_array src1_lines;
3142 if (!file_utils::read_text_file(src1_filename.get_ptr(), src1_lines, file_utils::cRTFTrim | file_utils::cRTFIgnoreEmptyLines | file_utils::cRTFIgnoreCommentedLines | file_utils::cRTFPrintErrorMessages))
3144 console::error("%s: Failed reading source file %s!\n", VOGL_FUNCTION_NAME, src1_filename.get_ptr());
3148 vogl_printf("Read 1st source file \"%s\", %u lines\n", src1_filename.get_ptr(), src1_lines.size());
3150 dynamic_string_array src2_lines;
3151 if (!file_utils::read_text_file(src2_filename.get_ptr(), src2_lines, file_utils::cRTFTrim | file_utils::cRTFIgnoreEmptyLines | file_utils::cRTFIgnoreCommentedLines | file_utils::cRTFPrintErrorMessages))
3153 console::error("%s: Failed reading source file %s!\n", VOGL_FUNCTION_NAME, src2_filename.get_ptr());
3157 vogl_printf("Read 2nd source file \"%s\", %u lines\n", src2_filename.get_ptr(), src2_lines.size());
3159 const uint64_t sum_comp_thresh = g_command_line_params.get_value_as_uint64("sum_compare_threshold");
3161 const uint compare_first_frame = g_command_line_params.get_value_as_uint("compare_first_frame");
3162 if (compare_first_frame > src2_lines.size())
3164 vogl_error_printf("%s: -compare_first_frame is %u, but the second file only has %u frames!\n", VOGL_FUNCTION_NAME, compare_first_frame, src2_lines.size());
3168 const uint lines_to_comp = math::minimum(src1_lines.size(), src2_lines.size() - compare_first_frame);
3170 if (src1_lines.size() != src2_lines.size())
3172 // FIXME: When we replay q2, we get 2 more frames vs. tracing. Not sure why, this needs to be investigated.
3173 if ( (!g_command_line_params.get_value_as_bool("ignore_line_count_differences")) && (labs(src1_lines.size() - src2_lines.size()) > 3) )
3175 vogl_error_printf("%s: Input files have a different number of lines! (%u vs %u)\n", VOGL_FUNCTION_NAME, src1_lines.size(), src2_lines.size());
3180 vogl_warning_printf("%s: Input files have a different number of lines! (%u vs %u)\n", VOGL_FUNCTION_NAME, src1_lines.size(), src2_lines.size());
3184 const uint compare_ignore_frames = g_command_line_params.get_value_as_uint("compare_ignore_frames");
3185 if (compare_ignore_frames > lines_to_comp)
3187 vogl_error_printf("%s: -compare_ignore_frames is too large!\n", VOGL_FUNCTION_NAME);
3191 const bool sum_hashing = g_command_line_params.get_value_as_bool("sum_hashing");
3193 if (g_command_line_params.has_key("compare_expected_frames"))
3195 const uint compare_expected_frames = g_command_line_params.get_value_as_uint("compare_expected_frames");
3196 if ((src1_lines.size() != compare_expected_frames) || (src2_lines.size() != compare_expected_frames))
3198 vogl_warning_printf("%s: Expected %u frames! First file has %u frames, second file has %u frames.\n", VOGL_FUNCTION_NAME, compare_expected_frames, src1_lines.size(), src2_lines.size());
3203 uint total_mismatches = 0;
3204 uint64_t max_sum_delta = 0;
3206 for (uint i = compare_ignore_frames; i < lines_to_comp; i++)
3208 const char *pStr1 = src1_lines[i].get_ptr();
3209 const char *pStr2 = src2_lines[i + compare_first_frame].get_ptr();
3211 uint64_t val1 = 0, val2 = 0;
3212 if (!string_ptr_to_uint64(pStr1, val1))
3214 vogl_error_printf("%s: Failed parsing line at index %u of first source file!\n", VOGL_FUNCTION_NAME, i);
3218 if (!string_ptr_to_uint64(pStr2, val2))
3220 vogl_error_printf("%s: Failed parsing line at index %u of second source file!\n", VOGL_FUNCTION_NAME, i);
3224 bool mismatch = false;
3230 delta = val1 - val2;
3232 delta = val2 - val1;
3233 max_sum_delta = math::maximum(max_sum_delta, delta);
3235 if (delta > sum_comp_thresh)
3240 mismatch = val1 != val2;
3246 vogl_error_printf("Mismatch at frame %u: %" PRIu64 ", %" PRIu64 "\n", i, val1, val2);
3248 vogl_error_printf("Mismatch at frame %u: 0x%" PRIX64 ", 0x%" PRIX64 "\n", i, val1, val2);
3254 vogl_printf("Max sum delta: %" PRIu64 "\n", max_sum_delta);
3256 if (!total_mismatches)
3257 vogl_printf("No mismatches\n");
3260 vogl_error_printf("%u total mismatches!\n", total_mismatches);
3267 //----------------------------------------------------------------------------------------------------------------------
3268 // tool_unpack_json_mode
3269 //----------------------------------------------------------------------------------------------------------------------
3270 static bool tool_unpack_json_mode()
3274 dynamic_string input_filename(g_command_line_params.get_value_as_string_or_empty("", 1));
3275 if (input_filename.is_empty())
3277 vogl_error_printf("Must specify filename of input UBJ file!\n");
3281 dynamic_string output_filename(g_command_line_params.get_value_as_string_or_empty("", 2));
3282 if (output_filename.is_empty())
3284 vogl_error_printf("Must specify filename of output text file!\n");
3289 vogl_message_printf("Reading UBJ file \"%s\"\n", input_filename.get_ptr());
3291 if (!doc.binary_deserialize_file(input_filename.get_ptr()))
3293 vogl_error_printf("Unable to deserialize input file \"%s\"!\n", input_filename.get_ptr());
3294 if (doc.get_error_msg().has_content())
3295 vogl_error_printf("%s\n", doc.get_error_msg().get_ptr());
3299 if (!doc.serialize_to_file(output_filename.get_ptr()))
3301 vogl_error_printf("Failed serializing to output file \"%s\"!\n", output_filename.get_ptr());
3305 vogl_message_printf("Wrote textual JSON file to \"%s\"\n", output_filename.get_ptr());
3310 //----------------------------------------------------------------------------------------------------------------------
3311 // tool_pack_json_mode
3312 //----------------------------------------------------------------------------------------------------------------------
3313 static bool tool_pack_json_mode()
3317 dynamic_string input_filename(g_command_line_params.get_value_as_string_or_empty("", 1));
3318 if (input_filename.is_empty())
3320 vogl_error_printf("Must specify filename of input text file!\n");
3324 dynamic_string output_filename(g_command_line_params.get_value_as_string_or_empty("", 2));
3325 if (output_filename.is_empty())
3327 vogl_error_printf("Must specify filename of output UBJ file!\n");
3332 vogl_message_printf("Reading JSON text file \"%s\"\n", input_filename.get_ptr());
3334 if (!doc.deserialize_file(input_filename.get_ptr()))
3336 vogl_error_printf("Unable to deserialize input file \"%s\"!\n", input_filename.get_ptr());
3337 if (doc.get_error_msg().has_content())
3338 vogl_error_printf("%s (Line: %u)\n", doc.get_error_msg().get_ptr(), doc.get_error_line());
3342 if (!doc.binary_serialize_to_file(output_filename.get_ptr()))
3344 vogl_error_printf("Failed serializing to output file \"%s\"!\n", output_filename.get_ptr());
3348 vogl_message_printf("Wrote binary UBJ file to \"%s\"\n", output_filename.get_ptr());
3353 //----------------------------------------------------------------------------------------------------------------------
3355 //----------------------------------------------------------------------------------------------------------------------
3356 static int xerror_handler(Display *dsp, XErrorEvent *error)
3358 char error_string[256];
3359 XGetErrorText(dsp, error->error_code, error_string, sizeof(error_string));
3361 fprintf(stderr, "voglreplay: Fatal X Windows Error: %s\n", error_string);
3365 //----------------------------------------------------------------------------------------------------------------------
3367 //----------------------------------------------------------------------------------------------------------------------
3368 int main(int argc, char *argv[])
3370 #if VOGL_FUNCTION_TRACING
3373 setvbuf(stdout, NULL, _IONBF, 0);
3374 setvbuf(stderr, NULL, _IONBF, 0);
3379 XSetErrorHandler(xerror_handler);
3381 if (!vogl_replay_init(argc, argv))
3383 vogl_replay_deinit();
3384 return EXIT_FAILURE;
3388 vogl_init_listener();
3389 #endif // VOGL_REMOTING
3393 return EXIT_SUCCESS;
3396 if (g_command_line_params.get_count("") < 2)
3398 vogl_error_printf("Must specify at least one trace (or input) files!\n");
3402 vogl_replay_deinit();
3403 return EXIT_FAILURE;
3406 if (g_command_line_params.get_value_as_bool("pause"))
3408 vogl_message_printf("Press key to continue\n");
3413 bool success = false;
3415 if (g_command_line_params.get_value_as_bool("dump"))
3417 tmZone(TELEMETRY_LEVEL0, TMZF_NONE, "dump");
3418 vogl_message_printf("Dump from binary to JSON mode\n");
3420 success = tool_dump_mode();
3422 else if (g_command_line_params.get_value_as_bool("parse"))
3424 tmZone(TELEMETRY_LEVEL0, TMZF_NONE, "parse");
3425 vogl_message_printf("Parse from JSON to binary mode\n");
3427 success = tool_parse_mode();
3429 else if (g_command_line_params.get_value_as_bool("info"))
3431 tmZone(TELEMETRY_LEVEL0, TMZF_NONE, "info");
3432 vogl_message_printf("Dumping trace information\n");
3434 success = tool_info_mode();
3436 else if (g_command_line_params.get_value_as_bool("unpack_json"))
3438 tmZone(TELEMETRY_LEVEL0, TMZF_NONE, "unpack_json");
3439 vogl_message_printf("Unpacking UBJ to text\n");
3441 success = tool_unpack_json_mode();
3443 else if (g_command_line_params.get_value_as_bool("pack_json"))
3445 tmZone(TELEMETRY_LEVEL0, TMZF_NONE, "pack_json");
3446 vogl_message_printf("Packing textual JSON to UBJ\n");
3448 success = tool_pack_json_mode();
3450 else if (g_command_line_params.get_value_as_bool("find"))
3452 vogl_message_printf("Find mode\n");
3454 success = tool_find_mode();
3456 else if (g_command_line_params.get_value_as_bool("compare_hash_files"))
3458 vogl_message_printf("Comparing hash/sum files\n");
3460 success = tool_compare_hash_files();
3464 tmZone(TELEMETRY_LEVEL0, TMZF_NONE, "replay_mode");
3465 vogl_message_printf("Replay mode\n");
3467 success = tool_replay_mode();
3470 console::printf("%u warning(s), %u error(s)\n", console::get_total_messages(cWarningConsoleMessage), console::get_total_messages(cErrorConsoleMessage));
3472 vogl_replay_deinit();
3474 return success ? EXIT_SUCCESS : EXIT_FAILURE;