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 replay window's initial width (default is 1024)" },
82 { "height", 1, false, "Replay: Set replay window's initial height (default is 768)" },
83 { "msaa", 1, false, "Replay: Set replay window's multisamples (default is 0)." },
84 { "lock_window_dimensions", 0, false, "Replay: Don't automatically change window's dimensions during replay" },
85 { "trim_file", 1, false, "Replay: Create a trimmed trace file during replay, must also specify -trim_frame" },
86 { "trim_frame", 1, false, "Replay: Frame index to begin trim, 0=beginning of trace, 1=first API call after first swap, etc." },
87 { "trim_len", 1, false, "Replay: Length of trim file, default=1 frame" },
88 { "multitrim", 0, false, "Replay trimming: Trim each frame to a different file" },
89 { "multitrim_interval", 1, false, "Replay trimming: Set the # of frames between each multitrimmed frame (default is 1)" },
90 { "no_trim_optimization", 0, false, "Replay trimming: If specified, do not remove unused programs, shaders, etc. from trim file" },
91 { "trim_call", 1, false, "Replay: Call counter index to begin trim" },
92 { "write_snapshot_call", 1, false, "Replay: Write JSON snapshot at the specified call counter index" },
93 { "write_snapshot_file", 1, false, "Replay: Write JSON snapshot to specified filename, must also specify --write_snapshot_call" },
94 { "write_snapshot_blobs", 0, false, "Replay: Write JSON snapshot blob files, must also specify --write_snapshot_call" },
95 { "endless", 0, false, "Replay: Loop replay endlessly instead of exiting" },
96 { "hash_backbuffer", 0, false, "Replay: Hash and output backbuffer CRC before every swap" },
97 { "dump_backbuffer_hashes", 1, false, "Replay: Dump backbuffer hashes to a text file" },
98 { "sum_hashing", 0, false, "Replay: Use per-component sums, instead of CRC hashing (useful for multisampling)" },
99 { "dump_screenshots", 0, false, "Replay: Dump backbuffer screenshot before every swap to numbered PNG files" },
100 { "dump_screenshots_prefix", 1, false, "Replay: Set PNG screenshot file prefix" },
101 { "swap_sleep", 1, false, "Replay: Sleep for X milliseconds after every swap" },
102 { "dump_packets_on_error", 0, false, "Replay: Dump GL trace packets as JSON to stdout on replay errors" },
103 { "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" },
104 { "dump_all_packets", 0, false, "Replay: Dump all GL trace packets as JSON to stdout" },
105 { "dump_shaders_on_draw", 0, false, "Replay: Dump shader source on draw calls" },
106 { "dump_framebuffer_on_draw", 0, false, "Replay: Dump framebuffer to PNG files after each draw/glEnd/glCallList" },
107 { "dump_framebuffer_on_draw_prefix", 1, false, "Replay: Base path/filename to use for --dump_framebuffer_on_draw" },
108 { "dump_framebuffer_on_draw_frame", 1, false, "Replay: Limit dumping framebuffer PNG files" },
109 { "dump_framebuffer_on_draw_first_gl_call", 1, false, "Replay: Limit dumping framebuffer PNG files" },
110 { "dump_framebuffer_on_draw_last_gl_call", 1, false, "Replay: Limit dumping framebuffer PNG files" },
111 { "force_debug_context", 0, false, "Replay: Force GL debug contexts" },
112 { "pause_on_exit", 0, false, "Replay: Wait for a keypress on exit" },
113 { "debug_test_snapshot_serialization", 0, false, "Interactive Replay Mode: Immediately serialize/deserialize state snapshots after taking them" },
114 { "pause_on_frame", 1, false, "Replay interactive mode: Pause on specified frame" },
115 { "interactive", 0, false, "Replay mode: Enable keyboard keys" },
116 { "disable_snapshot_caching", 0, false, "Replay mode: Disable caching of all state snapshot files, so they can be manually modified during replay" },
117 { "benchmark", 0, false, "Replay mode: Disable glGetError()'s, divergence checks, during replaying" },
118 { "keyframe_base_filename", 1, false, "Replay: Set base filename of trimmed replay keyframes, used for fast seeking" },
120 { "telemetry_level", 1, false, "Set Telemetry level." },
122 { "loop_frame", 1, false, "Replay: loop mode's start frame" },
123 { "loop_len", 1, false, "Replay: loop mode's loop length" },
124 { "loop_count", 1, false, "Replay: loop mode's loop count" },
125 { "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" },
128 { "find_func", 1, false, "Find: Limit the find to only the specified function name POSIX regex pattern" },
129 { "find_param", 1, false, "Find: The parameter value to find, hex, decimal integers, or GL enum strings OK" },
130 { "find_namespace", 1, false, "Find: Limits --find_param 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" },
131 { "find_param_name", 1, false, "Find: Limits the find to only params with the specified name (specify \"return\" to limit search to only return values)" },
132 { "find_frame_low", 1, false, "Find: Limit the find to frames beginning at the specified frame index" },
133 { "find_frame_high", 1, false, "Find: Limit the find to frames up to and including the specified frame index" },
134 { "find_call_low", 1, false, "Find: Limit the find to GL calls beginning at the specified call index" },
135 { "find_call_high", 1, false, "Find: Limit the find to GL calls up to and including the specified call index" },
137 // compare_hash_files specific
138 { "sum_compare_threshold", 1, false, "compare_hash_files: Only report mismatches greater than the specified threshold, use with --sum_hashing" },
139 { "compare_ignore_frames", 1, false, "compare_hash_files: Ignore first X frames" },
140 { "compare_expected_frames", 1, false, "compare_hash_files: Fail if the # of frames is not X" },
141 { "compare_first_frame", 1, false, "compare_hash_files: First frame to compare to in second hash file" },
142 { "ignore_line_count_differences", 0, false, "compare_hash_files: Don't stop if the # of lines differs between the two files" },
145 { "verify", 0, false, "Dump: Fully round-trip verify all JSON objects vs. the original packet's" },
146 { "no_blobs", 0, false, "Dump: Don't write binary blob files" },
147 { "write_debug_info", 0, false, "Dump: Write extra debug info to output JSON trace files" },
148 { "loose_file_path", 1, false, "Prefer reading trace blob files from this directory vs. the archive referred to or present in the trace file" },
149 { "debug", 0, false, "Enable verbose debug information" },
150 { "logfile", 1, false, "Create logfile" },
151 { "logfile_append", 1, false, "Append output to logfile" },
152 { "help", 0, false, "Display this help" },
153 { "?", 0, false, "Display this help" },
154 { "replay_debug", 0, false, "Enable various debug/verification code in the replayer" },
155 { "pause", 0, false, "Wait for a key at startup (so a debugger can be attached)" },
156 { "verbose", 0, false, "Verbose debug output" },
157 { "quiet", 0, false, "Disable all console output" },
158 { "gl_debug_log", 0, false, "Dump GL prolog/epilog messages to stdout (very slow - helpful to narrow down driver crashes)" },
159 { "vogl_func_tracing", 0, false, NULL },
162 static command_line_param_desc g_command_line_interactive_descs[] =
164 { "s", 0, false, "slow mode" },
165 { "<space>", 0, false, "pause" },
166 { "r", 0, false, "rewind to beginning" },
167 { "e", 0, false, "seek to last frame" },
168 { "t", 0, false, "trim frame" },
169 { "j", 0, false, "trim and play json" },
170 { "<left>", 0, false, "step left" },
171 { "<right>", 0, false, "step right" },
174 //----------------------------------------------------------------------------------------------------------------------
176 //----------------------------------------------------------------------------------------------------------------------
177 static bool init_logfile()
181 dynamic_string backbuffer_hash_file;
182 if (g_command_line_params.get_value_as_string(backbuffer_hash_file, "dump_backbuffer_hashes"))
184 remove(backbuffer_hash_file.get_ptr());
185 vogl_message_printf("Deleted backbuffer hash file \"%s\"\n", backbuffer_hash_file.get_ptr());
188 dynamic_string log_file(g_command_line_params.get_value_as_string_or_empty("logfile"));
189 dynamic_string log_file_append(g_command_line_params.get_value_as_string_or_empty("logfile_append"));
190 if (log_file.is_empty() && log_file_append.is_empty())
193 dynamic_string filename(log_file_append.is_empty() ? log_file : log_file_append);
195 // This purposely leaks, don't care
196 g_vogl_pLog_stream = vogl_new(cfile_stream);
198 if (!g_vogl_pLog_stream->open(filename.get_ptr(), cDataStreamWritable, !log_file_append.is_empty()))
200 vogl_error_printf("%s: Failed opening log file \"%s\"\n", VOGL_FUNCTION_NAME, filename.get_ptr());
205 vogl_message_printf("Opened log file \"%s\"\n", filename.get_ptr());
207 console::set_log_stream(g_vogl_pLog_stream);
213 //----------------------------------------------------------------------------------------------------------------------
215 //----------------------------------------------------------------------------------------------------------------------
216 static void tool_print_title()
220 printf("voglreplay ");
221 if (sizeof(void *) > 4)
222 console::printf("64-bit ");
224 console::printf("32-bit ");
225 #ifdef VOGL_BUILD_DEBUG
226 console::printf("Debug ");
228 console::printf("Release ");
230 console::printf("Built %s %s\n", __DATE__, __TIME__);
233 //----------------------------------------------------------------------------------------------------------------------
235 //----------------------------------------------------------------------------------------------------------------------
236 static void tool_print_help()
240 console::printf("Usage: voglreplay [ -option ... ] input_file optional_output_file [ -option ... ]\n");
241 console::printf("Command line options may begin with single minus \"-\" or double minus \"--\"\n");
243 console::printf("\nCommand line options:\n");
245 dump_command_line_info(VOGL_ARRAY_SIZE(g_command_line_param_descs), g_command_line_param_descs, "--");
247 console::printf("\nInteractive replay mode keys:\n");
248 dump_command_line_info(VOGL_ARRAY_SIZE(g_command_line_interactive_descs), g_command_line_interactive_descs, " ");
251 //----------------------------------------------------------------------------------------------------------------------
252 // init_command_line_params
253 //----------------------------------------------------------------------------------------------------------------------
254 static bool init_command_line_params(int argc, char *argv[])
258 command_line_params::parse_config parse_cfg;
259 parse_cfg.m_single_minus_params = true;
260 parse_cfg.m_double_minus_params = true;
262 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))
264 vogl_error_printf("%s: Failed parsing command line parameters!\n", VOGL_FUNCTION_NAME);
271 if (g_command_line_params.get_value_as_bool("help") || g_command_line_params.get_value_as_bool("?"))
280 //----------------------------------------------------------------------------------------------------------------------
282 //----------------------------------------------------------------------------------------------------------------------
283 static bool load_gl()
287 g_actual_libgl_module_handle = dlopen("libGL.so.1", RTLD_LAZY);
288 if (!g_actual_libgl_module_handle)
290 vogl_error_printf("%s: Failed loading libGL.so.1!\n", VOGL_FUNCTION_NAME);
294 GL_ENTRYPOINT(glXGetProcAddress) = reinterpret_cast<glXGetProcAddress_func_ptr_t>(dlsym(g_actual_libgl_module_handle, "glXGetProcAddress"));
295 if (!GL_ENTRYPOINT(glXGetProcAddress))
297 vogl_error_printf("%s: Failed getting address of glXGetProcAddress() from libGL.so.1!\n", VOGL_FUNCTION_NAME);
304 //----------------------------------------------------------------------------------------------------------------------
305 // vogl_get_proc_address_helper
306 //----------------------------------------------------------------------------------------------------------------------
307 static vogl_void_func_ptr_t vogl_get_proc_address_helper(const char *pName)
311 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;
313 if ((!pFunc) && (GL_ENTRYPOINT(glXGetProcAddress)))
314 pFunc = reinterpret_cast<vogl_void_func_ptr_t>(GL_ENTRYPOINT(glXGetProcAddress)(reinterpret_cast<const GLubyte *>(pName)));
320 // HACK HACK - for testing
321 static void vogl_direct_gl_func_prolog(gl_entrypoint_id_t entrypoint_id, void *pUser_data, void **pStack_data)
323 printf("*** PROLOG %u\n", entrypoint_id);
326 void vogl_direct_gl_func_epilog(gl_entrypoint_id_t entrypoint_id, void *pUser_data, void **pStack_data)
328 printf("*** EPILOG %u\n", entrypoint_id);
332 //----------------------------------------------------------------------------------------------------------------------
333 // vogl_direct_gl_func_prolog - This function is called before EVERY single GL/GLX function call we make.
334 //----------------------------------------------------------------------------------------------------------------------
335 static void vogl_direct_gl_func_prolog(gl_entrypoint_id_t entrypoint_id, void *pUser_data, void **ppStack_data)
337 VOGL_NOTE_UNUSED(entrypoint_id);
338 VOGL_NOTE_UNUSED(pUser_data);
339 VOGL_NOTE_UNUSED(ppStack_data);
341 printf("* GLPROLOG %s\n", g_vogl_entrypoint_descs[entrypoint_id].m_pName);
344 //----------------------------------------------------------------------------------------------------------------------
345 // vogl_direct_gl_func_epilog - This function is called immediately after EVERY single GL/GLX function call we make.
346 //----------------------------------------------------------------------------------------------------------------------
347 static void vogl_direct_gl_func_epilog(gl_entrypoint_id_t entrypoint_id, void *pUser_data, void **ppStack_data)
349 VOGL_NOTE_UNUSED(entrypoint_id);
350 VOGL_NOTE_UNUSED(pUser_data);
351 VOGL_NOTE_UNUSED(ppStack_data);
353 printf("* GLEPILOG %s\n", g_vogl_entrypoint_descs[entrypoint_id].m_pName);
356 //----------------------------------------------------------------------------------------------------------------------
358 //----------------------------------------------------------------------------------------------------------------------
359 static bool vogl_replay_init(int argc, char *argv[])
363 g_thread_safe_random.seed_from_urandom();
365 colorized_console::init();
366 colorized_console::set_exception_callback();
367 //console::set_tool_prefix("(voglreplay) ");
371 if (!init_command_line_params(argc, argv))
375 int telemetry_level = g_command_line_params.get_value_as_int("telemetry_level", 0,
376 TELEMETRY_LEVEL_MIN + 1, TELEMETRY_LEVEL_MIN, TELEMETRY_LEVEL_MAX);
377 telemetry_set_level(telemetry_level);
381 vogl_common_lib_early_init();
382 vogl_common_lib_global_init();
384 if (g_command_line_params.get_value_as_bool("quiet"))
385 console::disable_output();
387 if (g_command_line_params.get_value_as_bool("gl_debug_log"))
389 vogl_set_direct_gl_func_prolog(vogl_direct_gl_func_prolog, NULL);
390 vogl_set_direct_gl_func_epilog(vogl_direct_gl_func_epilog, NULL);
396 bool wrap_all_gl_calls = true;
398 if (g_command_line_params.get_value_as_bool("benchmark"))
399 wrap_all_gl_calls = false;
401 vogl_init_actual_gl_entrypoints(vogl_get_proc_address_helper, wrap_all_gl_calls);
404 // HACK HACK - for testing
405 vogl_set_direct_gl_func_prolog(vogl_direct_gl_func_prolog, NULL);
406 vogl_set_direct_gl_func_epilog(vogl_direct_gl_func_epilog, NULL);
412 //----------------------------------------------------------------------------------------------------------------------
413 // vogl_replay_deinit
414 //----------------------------------------------------------------------------------------------------------------------
415 static void vogl_replay_deinit()
419 colorized_console::deinit();
422 //----------------------------------------------------------------------------------------------------------------------
423 // X11_Pending - from SDL
424 //----------------------------------------------------------------------------------------------------------------------
425 static int X11_Pending(Display *display)
429 /* Flush the display connection and look to see if events are queued */
431 if (XEventsQueued(display, QueuedAlready))
436 /* More drastic measures are required -- see if X is ready to talk */
438 static struct timeval zero_time; /* static == 0 */
442 x11_fd = ConnectionNumber(display);
444 FD_SET(x11_fd, &fdset);
445 if (select(x11_fd + 1, &fdset, NULL, NULL, &zero_time) == 1)
447 return (XPending(display));
451 /* Oh well, nothing is ready .. */
455 //----------------------------------------------------------------------------------------------------------------------
456 // read_state_snapshot_from_trace
457 //----------------------------------------------------------------------------------------------------------------------
458 static vogl_gl_state_snapshot *read_state_snapshot_from_trace(dynamic_string filename)
462 timed_scope ts(VOGL_FUNCTION_NAME);
464 vogl_gl_state_snapshot *pSnapshot = NULL;
466 vogl_loose_file_blob_manager file_blob_manager;
467 dynamic_string keyframe_trace_path(file_utils::get_pathname(filename.get_ptr()));
468 file_blob_manager.init(cBMFReadable, keyframe_trace_path.get_ptr());
470 dynamic_string actual_keyframe_filename;
471 vogl_unique_ptr<vogl_trace_file_reader> pTrace_reader(vogl_open_trace_file(filename, actual_keyframe_filename, NULL));
472 if (!pTrace_reader.get())
474 vogl_error_printf("%s: Failed reading keyframe file %s!\n", VOGL_FUNCTION_NAME, filename.get_ptr());
478 vogl_ctypes trace_gl_ctypes(pTrace_reader->get_sof_packet().m_pointer_sizes);
480 vogl_trace_packet keyframe_trace_packet(&trace_gl_ctypes);
482 bool found_snapshot = false;
485 vogl_trace_file_reader::trace_file_reader_status_t read_status = pTrace_reader->read_next_packet();
487 if ((read_status != vogl_trace_file_reader::cOK) && (read_status != vogl_trace_file_reader::cEOF))
489 vogl_error_printf("%s: Failed reading from keyframe trace file!\n", VOGL_FUNCTION_NAME);
493 if ((read_status == vogl_trace_file_reader::cEOF) || (pTrace_reader->get_packet_type() == cTSPTEOF))
495 vogl_error_printf("%s: Failed finding state snapshot in keyframe file!\n", VOGL_FUNCTION_NAME);
499 if (pTrace_reader->get_packet_type() != cTSPTGLEntrypoint)
502 if (!keyframe_trace_packet.deserialize(pTrace_reader->get_packet_buf().get_ptr(), pTrace_reader->get_packet_buf().size(), false))
504 vogl_error_printf("%s: Failed parsing GL entrypoint packet in keyframe file\n", VOGL_FUNCTION_NAME);
508 const vogl_trace_gl_entrypoint_packet *pGL_packet = &pTrace_reader->get_packet<vogl_trace_gl_entrypoint_packet>();
509 gl_entrypoint_id_t entrypoint_id = static_cast<gl_entrypoint_id_t>(pGL_packet->m_entrypoint_id);
511 //const gl_entrypoint_desc_t &entrypoint_desc = g_vogl_entrypoint_descs[entrypoint_id];
513 if (vogl_is_swap_buffers_entrypoint(entrypoint_id) || vogl_is_draw_entrypoint(entrypoint_id) || vogl_is_make_current_entrypoint(entrypoint_id))
515 vogl_error_printf("Failed finding state snapshot in keyframe file!\n");
519 switch (entrypoint_id)
521 case VOGL_ENTRYPOINT_glInternalTraceCommandRAD:
523 GLuint cmd = keyframe_trace_packet.get_param_value<GLuint>(0);
524 GLuint size = keyframe_trace_packet.get_param_value<GLuint>(1);
525 VOGL_NOTE_UNUSED(size);
527 if (cmd == cITCRKeyValueMap)
529 key_value_map &kvm = keyframe_trace_packet.get_key_value_map();
531 dynamic_string cmd_type(kvm.get_string("command_type"));
532 if (cmd_type == "state_snapshot")
534 dynamic_string id(kvm.get_string("binary_id"));
537 vogl_error_printf("%s: Missing binary_id field in glInternalTraceCommandRAD key_valye_map command type: \"%s\"\n", VOGL_FUNCTION_NAME, cmd_type.get_ptr());
541 uint8_vec snapshot_data;
543 timed_scope ts("get_multi_blob_manager().get");
544 if (!pTrace_reader->get_multi_blob_manager().get(id, snapshot_data) || (snapshot_data.is_empty()))
546 vogl_error_printf("%s: Failed reading snapshot blob data \"%s\"!\n", VOGL_FUNCTION_NAME, id.get_ptr());
551 vogl_message_printf("%s: Deserializing state snapshot \"%s\", %u bytes\n", VOGL_FUNCTION_NAME, id.get_ptr(), snapshot_data.size());
555 timed_scope ts("doc.binary_deserialize");
556 if (!doc.binary_deserialize(snapshot_data) || (!doc.get_root()))
558 vogl_error_printf("%s: Failed deserializing JSON snapshot blob data \"%s\"!\n", VOGL_FUNCTION_NAME, id.get_ptr());
563 pSnapshot = vogl_new(vogl_gl_state_snapshot);
565 timed_scope ts("pSnapshot->deserialize");
566 if (!pSnapshot->deserialize(*doc.get_root(), pTrace_reader->get_multi_blob_manager(), &trace_gl_ctypes))
568 vogl_delete(pSnapshot);
571 vogl_error_printf("%s: Failed deserializing snapshot blob data \"%s\"!\n", VOGL_FUNCTION_NAME, id.get_ptr());
575 found_snapshot = true;
585 } while (!found_snapshot);
590 //----------------------------------------------------------------------------------------------------------------------
591 // get_replayer_flags_from_command_line_params
592 //----------------------------------------------------------------------------------------------------------------------
593 static uint get_replayer_flags_from_command_line_params(bool interactive_mode)
595 uint replayer_flags = 0;
599 const char *m_pCommand;
601 } s_replayer_command_line_params[] =
603 { "benchmark", cGLReplayerBenchmarkMode },
604 { "verbose", cGLReplayerVerboseMode },
605 { "force_debug_context", cGLReplayerForceDebugContexts },
606 { "dump_all_packets", cGLReplayerDumpAllPackets },
607 { "debug", cGLReplayerDebugMode },
608 { "lock_window_dimensions", cGLReplayerLockWindowDimensions },
609 { "replay_debug", cGLReplayerLowLevelDebugMode },
610 { "dump_packet_blob_files_on_error", cGLReplayerDumpPacketBlobFilesOnError },
611 { "dump_shaders_on_draw", cGLReplayerDumpShadersOnDraw },
612 { "dump_packets_on_error", cGLReplayerDumpPacketsOnError },
613 { "dump_screenshots", cGLReplayerDumpScreenshots },
614 { "hash_backbuffer", cGLReplayerHashBackbuffer },
615 { "dump_backbuffer_hashes", cGLReplayerDumpBackbufferHashes },
616 { "sum_hashing", cGLReplayerSumHashing },
617 { "dump_framebuffer_on_draw", cGLReplayerDumpFramebufferOnDraws },
620 for (uint i = 0; i < sizeof(s_replayer_command_line_params) / sizeof(s_replayer_command_line_params[0]); i++)
621 if (g_command_line_params.get_value_as_bool(s_replayer_command_line_params[i].m_pCommand))
622 replayer_flags |= s_replayer_command_line_params[i].m_flag;
624 if (interactive_mode && !g_command_line_params.get_value_as_bool("disable_snapshot_caching"))
625 replayer_flags |= cGLReplayerSnapshotCaching;
627 return replayer_flags;
630 //----------------------------------------------------------------------------------------------------------------------
632 //----------------------------------------------------------------------------------------------------------------------
633 static bool tool_replay_mode()
637 dynamic_string trace_filename(g_command_line_params.get_value_as_string_or_empty("", 1));
638 if (trace_filename.is_empty())
640 vogl_error_printf("%s: No trace file specified!\n", VOGL_FUNCTION_NAME);
644 dynamic_string actual_trace_filename;
645 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()));
646 if (!pTrace_reader.get())
648 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());
652 vogl_printf("Reading trace file %s\n", actual_trace_filename.get_ptr());
654 bool interactive_mode = g_command_line_params.get_value_as_bool("interactive");
656 vogl_gl_replayer replayer;
658 uint replayer_flags = get_replayer_flags_from_command_line_params(interactive_mode);
660 vogl_replay_window window;
662 // TODO: This will create a window with default attributes, which seems fine for the majority of traces.
663 // 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.
664 // Also, this design only supports a single window, which is going to be a problem with multiple window traces.
665 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), g_command_line_params.get_value_as_int("msaa", 0, 0, 0, 65535)))
667 vogl_error_printf("%s: Failed initializing replay window\n", VOGL_FUNCTION_NAME);
671 if (!replayer.init(replayer_flags, &window, pTrace_reader->get_sof_packet(), pTrace_reader->get_multi_blob_manager()))
673 vogl_error_printf("%s: Failed initializing GL replayer\n", VOGL_FUNCTION_NAME);
677 if (replayer_flags & cGLReplayerBenchmarkMode)
679 // Also disable all glGetError() calls in vogl_utils.cpp.
680 vogl_disable_gl_get_error();
683 replayer.set_swap_sleep_time(g_command_line_params.get_value_as_uint("swap_sleep"));
684 replayer.set_dump_framebuffer_on_draw_prefix(g_command_line_params.get_value_as_string("dump_framebuffer_on_draw_prefix", 0, "screenshot"));
685 replayer.set_screenshot_prefix(g_command_line_params.get_value_as_string("dump_screenshots_prefix", 0, "screenshot"));
686 replayer.set_backbuffer_hash_filename(g_command_line_params.get_value_as_string_or_empty("dump_backbuffer_hashes"));
687 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));
688 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));
689 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));
691 XSelectInput(window.get_display(), window.get_xwindow(),
692 EnterWindowMask | LeaveWindowMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | ExposureMask | FocusChangeMask | KeyPressMask | KeyReleaseMask | PropertyChangeMask | StructureNotifyMask | KeymapStateMask);
694 Atom wmDeleteMessage = XInternAtom(window.get_display(), "WM_DELETE_WINDOW", False);
695 XSetWMProtocols(window.get_display(), window.get_xwindow(), &wmDeleteMessage, 1);
697 Bool win_mapped = false;
699 vogl_gl_state_snapshot *pSnapshot = NULL;
700 int64_t snapshot_loop_start_frame = -1;
701 int64_t snapshot_loop_end_frame = -1;
703 vogl::hash_map<uint64_t> keys_pressed, keys_down;
704 dynamic_string keyframe_base_filename(g_command_line_params.get_value_as_string("keyframe_base_filename"));
705 vogl::vector<uint64_t> keyframes;
706 int64_t paused_mode_frame_index = -1;
707 int64_t take_snapshot_at_frame_index = g_command_line_params.get_value_as_int64("pause_on_frame", 0, -1);
708 bool paused_mode = false;
709 bool slow_mode = false;
711 if (!keyframe_base_filename.is_empty())
714 if (!finder.find((keyframe_base_filename + "*.bin").get_ptr()))
716 vogl_error_printf("Failed finding files: %s\n", keyframe_base_filename.get_ptr());
720 for (uint i = 0; i < finder.get_files().size(); i++)
722 dynamic_string base_name(finder.get_files()[i].m_name);
723 dynamic_string ext(base_name);
724 file_utils::get_extension(ext);
728 file_utils::remove_extension(base_name);
729 int underscore_ofs = base_name.find_right('_');
730 if (underscore_ofs < 0)
733 dynamic_string frame_index_str(base_name.right(underscore_ofs + 1));
734 if (frame_index_str.is_empty())
737 const char *pFrame_index = frame_index_str.get_ptr();
739 uint64_t frame_index = 0;
740 if (!string_ptr_to_uint64(pFrame_index, frame_index))
743 keyframes.push_back(frame_index);
744 printf("Found keyframe file %s index %" PRIu64 "\n", finder.get_files()[i].m_fullname.get_ptr(), frame_index);
750 int loop_frame = g_command_line_params.get_value_as_int("loop_frame", 0, -1);
751 int loop_len = math::maximum<int>(g_command_line_params.get_value_as_int("loop_len", 0, 1), 1);
752 int loop_count = math::maximum<int>(g_command_line_params.get_value_as_int("loop_count", 0, cINT32_MAX), 1);
753 int draw_kill_max_thresh = g_command_line_params.get_value_as_int("draw_kill_max_thresh", 0, -1);
754 bool endless_mode = g_command_line_params.get_value_as_bool("endless");
756 bool multitrim_mode = g_command_line_params.get_value_as_bool("multitrim");
757 int multitrim_interval = g_command_line_params.get_value_as_int("multitrim_interval", 0, 1, 1);
758 int multitrim_frames_remaining = 0;
760 int64_t write_snapshot_index = g_command_line_params.get_value_as_int64("write_snapshot_call", 0, -1, 0);
761 dynamic_string write_snapshot_filename = g_command_line_params.get_value_as_string("write_snapshot_file", 0, "state_snapshot.json");
763 int64_t trim_call_index = g_command_line_params.get_value_as_int64("trim_call", 0, -1, 0);
764 vogl::vector<uint> trim_frames(g_command_line_params.get_count("trim_frame"));
765 for (uint i = 0; i < trim_frames.size(); i++)
767 bool parsed_successfully;
768 trim_frames[i] = g_command_line_params.get_value_as_uint("trim_frame", i, 0, 0, cUINT32_MAX, 0, &parsed_successfully);
769 if (!parsed_successfully)
771 vogl_error_printf("%s: Failed parsing -trim_frame at index %u\n", VOGL_FUNCTION_NAME, i);
776 vogl::vector<dynamic_string> trim_filenames(g_command_line_params.get_count("trim_file"));
777 for (uint i = 0; i < trim_filenames.size(); i++)
779 dynamic_string filename(g_command_line_params.get_value_as_string("trim_file", i));
781 if (filename.is_empty())
783 vogl_error_printf("%s: Invalid trim filename\n", VOGL_FUNCTION_NAME);
787 trim_filenames[i] = filename;
789 if (file_utils::add_default_extension(trim_filenames[i], ".bin"))
790 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());
793 vogl::vector<uint> trim_lens(g_command_line_params.get_count("trim_len"));
794 for (uint i = 0; i < trim_lens.size(); i++)
796 bool parsed_successfully;
797 trim_lens[i] = g_command_line_params.get_value_as_uint("trim_len", i, 1, 0, cUINT32_MAX, 0, &parsed_successfully);
798 if (!parsed_successfully)
800 vogl_error_printf("%s: Failed parsing -trim_len at index %u\n", VOGL_FUNCTION_NAME, i);
805 uint num_trim_files_written = 0;
806 uint highest_frame_to_trim = 0;
808 vogl_loose_file_blob_manager trim_file_blob_manager;
812 if (trim_frames.size())
814 if (trim_filenames.is_empty())
816 console::error("%s: -trim_frame specified without specifying at least one -trim_file or -trim_call\n", VOGL_FUNCTION_NAME);
821 if (write_snapshot_index >= 0)
823 if ((multitrim_mode) || (trim_frames.size()) || (trim_lens.size()) || (trim_call_index >= 0))
825 console::warning("%s: Can't write snapshot and trim at the same time, disabling trimming\n", VOGL_FUNCTION_NAME);
827 multitrim_mode = false;
830 trim_call_index = -1;
833 if (draw_kill_max_thresh > 0)
835 console::warning("%s: Write snapshot mode is enabled, disabling -draw_kill_max_thresh\n", VOGL_FUNCTION_NAME);
836 draw_kill_max_thresh = -1;
841 console::warning("%s: Write snapshot mode is enabled, disabling -endless\n", VOGL_FUNCTION_NAME);
842 endless_mode = false;
845 else if ((trim_frames.size()) || (trim_call_index != -1))
847 if (trim_filenames.is_empty())
849 console::error("%s: Must also specify at least one -trim_file\n", VOGL_FUNCTION_NAME);
853 if (trim_call_index != -1)
855 if (trim_frames.size())
857 console::error("%s: Can't specify both -trim_call and -trim_frame\n", VOGL_FUNCTION_NAME);
863 console::error("%s: Can't specify both -trim_call and -multitrim\n", VOGL_FUNCTION_NAME);
867 if (trim_filenames.size() > 1)
869 console::warning("%s: -trim_call specified but more than 1 -trim_file specified - ignoring all but first -trim_file's\n", VOGL_FUNCTION_NAME);
870 trim_filenames.resize(1);
874 trim_file_blob_manager.init(cBMFWritable);
876 if (trim_frames.size() > 1)
878 if ((trim_filenames.size() > 1) && (trim_filenames.size() != trim_frames.size()))
880 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);
884 if ((trim_lens.size() > 1) && (trim_lens.size() != trim_frames.size()))
886 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);
891 if ((multitrim_mode) && (trim_filenames.size() > 1))
893 console::warning("%s: Only 1 filename needs to be specified in -multitrim mode\n", VOGL_FUNCTION_NAME);
896 if (loop_frame != -1)
898 console::warning("%s: Trim is enabled, disabling -loop_frame\n", VOGL_FUNCTION_NAME);
904 if (draw_kill_max_thresh > 0)
906 console::warning("%s: Trim is enabled, disabling -draw_kill_max_thresh\n", VOGL_FUNCTION_NAME);
907 draw_kill_max_thresh = -1;
912 console::warning("%s: Trim is enabled, disabling -endless\n", VOGL_FUNCTION_NAME);
913 endless_mode = false;
916 if (trim_call_index == -1)
918 for (uint tf = 0; tf < trim_frames.size(); tf++)
921 if (trim_lens.size())
923 if (trim_lens.size() < trim_frames.size())
928 len = math::maximum(len, 1U);
929 highest_frame_to_trim = math::maximum<uint>(highest_frame_to_trim, trim_frames[tf] + len - 1);
935 if (trim_filenames.size())
936 console::warning("%s: -trim_file was specified, but -trim_frame was not specified so ignoring\n", VOGL_FUNCTION_NAME);
937 if (trim_lens.size())
938 console::warning("%s: -trim_len was specified, but -trim_frame was not specified so ignoring\n", VOGL_FUNCTION_NAME);
940 trim_filenames.clear();
948 tmZone(TELEMETRY_LEVEL0, TMZF_NONE, "Main Loop");
950 while (X11_Pending(window.get_display()))
954 // Watch for new X eventsn
955 XNextEvent(window.get_display(), &newEvent);
957 switch (newEvent.type)
961 KeySym xsym = XLookupKeysym(&newEvent.xkey, 0);
963 //printf("KeyPress 0%04" PRIX64 "%" PRIu64 "\n", (uint64_t)xsym, (uint64_t)xsym);
965 keys_down.insert(xsym);
966 keys_pressed.insert(xsym);
972 KeySym xsym = XLookupKeysym(&newEvent.xkey, 0);
974 //printf("KeyRelease 0x%04" PRIX64 " %" PRIu64 "\n", (uint64_t)xsym, (uint64_t)xsym);
976 keys_down.erase(xsym);
983 //printf("FocusIn/FocusOut\n");
991 //XRefreshKeyboardMapping(&newEvent);
996 //printf("UnmapNotify\n");
1006 //printf("MapNotify\n");
1012 if (!replayer.update_window_dimensions())
1017 case ConfigureNotify:
1019 if (!replayer.update_window_dimensions())
1026 vogl_message_printf("Exiting\n");
1031 if (newEvent.xclient.data.l[0] == (int)wmDeleteMessage)
1033 vogl_message_printf("Exiting\n");
1044 // TODO: Massively refactor this! Move the replayer into a class, etc.
1045 if (interactive_mode)
1053 tmZone(TELEMETRY_LEVEL0, TMZF_NONE, "Interactive");
1055 // 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
1056 // resizes - hopefully this complexity will go away once we get pbuffer's or whatever in.
1057 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);
1059 if (keys_pressed.contains('c'))
1061 keys_pressed.erase('c');
1062 if (replayer.is_valid())
1064 dynamic_string filename;
1065 for (uint i = 0; i < 10000000; i++)
1067 filename.format("screenshot_%06u.png", i);
1068 if (!file_utils::does_file_exist(filename.get_ptr()))
1072 replayer.dump_frontbuffer_screenshot_before_next_swap(filename);
1076 if (keys_pressed.contains('s'))
1078 keys_pressed.erase('s');
1079 slow_mode = !slow_mode;
1082 // When paused, we'll NOT be at a frame boundary because the prev. loop applied a state snapshot (which will be pending)
1083 if (replayer.get_at_frame_boundary() && !replayer.get_pending_apply_snapshot())
1085 bool take_new_snapshot = false;
1087 // See if we've scheduled a snapshot at this frame
1088 if ((int64_t)replayer.get_frame_index() == take_snapshot_at_frame_index)
1090 take_new_snapshot = true;
1092 take_snapshot_at_frame_index = -1;
1094 // Check for pausing
1095 else if (keys_pressed.contains(XK_space))
1097 keys_pressed.erase(XK_space);
1101 console::info("Unpausing\n");
1103 keys_pressed.erase(XK_space);
1105 vogl_delete(pSnapshot);
1108 paused_mode_frame_index = -1;
1110 paused_mode = false;
1114 console::info("Pausing\n");
1118 take_new_snapshot = true;
1122 // Snapshot the current state
1123 if (take_new_snapshot)
1125 vogl_delete(pSnapshot);
1128 pSnapshot = replayer.snapshot_state();
1131 vogl_error_printf("%s: Snapshot failed!\n", VOGL_FUNCTION_NAME);
1135 if (g_command_line_params.get_value_as_bool("debug_test_snapshot_serialization"))
1137 // Obviously, this crap is only for debugging.
1138 vogl_memory_blob_manager mem_blob_manager;
1139 mem_blob_manager.init(cBMFReadWrite);
1141 json_document temp_json_doc;
1142 if (!pSnapshot->serialize(*temp_json_doc.get_root(), mem_blob_manager, &replayer.get_trace_gl_ctypes()))
1144 console::error("%s: Failed serializing state snapshot!\n", VOGL_FUNCTION_NAME);
1149 temp_json_doc.binary_serialize(ubj_data);
1150 temp_json_doc.binary_deserialize(ubj_data);
1153 vogl_gl_state_snapshot *pNew_snapshot = vogl_new(vogl_gl_state_snapshot);
1155 if (!pNew_snapshot->deserialize(*temp_json_doc.get_root(), mem_blob_manager, &replayer.get_trace_gl_ctypes()))
1157 console::error("%s: Failed deserializing state snapshot!\n", VOGL_FUNCTION_NAME);
1161 vogl_delete(pSnapshot);
1163 pSnapshot = pNew_snapshot;
1168 paused_mode_frame_index = replayer.get_frame_index();
1174 // Begin processing the next frame
1175 bool applied_snapshot = false;
1176 vogl_gl_replayer::status_t status = replayer.process_pending_window_resize(&applied_snapshot);
1178 if (status == vogl_gl_replayer::cStatusOK)
1180 if (replayer.get_at_frame_boundary())
1182 const char *pWindow_name = (sizeof(void *) == sizeof(uint32)) ? "voglreplay 32-bit" : "voglreplay 64-bit";
1183 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)" : "");
1184 window.set_title(window_title.get_ptr());
1187 // 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.
1188 if (((applied_snapshot) || (replayer.get_at_frame_boundary())) &&
1189 (keys_pressed.contains('t') || keys_pressed.contains('j')))
1191 uint64_t frame_to_trim;
1193 frame_to_trim = paused_mode_frame_index;
1195 frame_to_trim = replayer.get_frame_index();
1197 dynamic_string trim_name;
1198 for (uint i = 0; i < 1000000; i++)
1200 trim_name.format("trim_%06" PRIu64 "_%u", frame_to_trim, i);
1201 if (!file_utils::does_dir_exist(trim_name.get_ptr()))
1205 if (!file_utils::create_directory(trim_name.get_ptr()))
1207 vogl_error_printf("%s: Failed creating trim directory %s\n", VOGL_FUNCTION_NAME, trim_name.get_ptr());
1211 vogl_loose_file_blob_manager trim_file_blob_manager;
1212 trim_file_blob_manager.init(cBMFReadWrite, trim_name.get_ptr());
1214 dynamic_string trim_filename(trim_name + "/" + trim_name + ".bin");
1215 dynamic_string snapshot_id;
1216 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);
1217 if (replayer.write_trim_file(write_trim_file_flags, trim_filename, 1, *pTrace_reader, &snapshot_id))
1219 dynamic_string json_trim_base_filename(trim_name + "/j" + trim_name);
1221 char voglreplay_exec_filename[1024];
1222 file_utils::get_exec_filename(voglreplay_exec_filename, sizeof(voglreplay_exec_filename));
1224 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());
1225 if (system(convert_to_json_spawn_str.get_ptr()) != 0)
1227 vogl_error_printf("%s: Failed running voglreplay: %s\n", VOGL_FUNCTION_NAME, convert_to_json_spawn_str.get_ptr());
1231 dynamic_string json_trim_full_filename(trim_name + "/j" + trim_name + "_000000.json");
1233 dynamic_string view_json_spawn_str(cVarArg, "np \"%s\" &", json_trim_full_filename.get_ptr());
1234 system(view_json_spawn_str.get_ptr());
1237 if (keys_pressed.contains('j'))
1239 dynamic_string workdir(".");
1240 file_utils::full_path(workdir);
1242 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());
1243 system(replay_json_spawn_str.get_ptr());
1248 keys_pressed.erase('t');
1249 keys_pressed.erase('j');
1252 // Now replay the next frame's GL commands up to the swap
1253 status = replayer.process_frame(*pTrace_reader);
1256 if (status == vogl_gl_replayer::cStatusHardFailure)
1259 if ((slow_mode) && (!paused_mode))
1262 int64_t seek_to_target_frame = -1;
1263 bool seek_to_closest_keyframe = false;
1264 int seek_to_closest_frame_dir_bias = 0;
1266 if (status == vogl_gl_replayer::cStatusAtEOF)
1268 vogl_message_printf("%s: At trace EOF, frame index %u\n", VOGL_FUNCTION_NAME, replayer.get_frame_index());
1270 // Right after the last swap in the file, either rewind or pause back on the last frame
1271 if ((paused_mode) && (replayer.get_frame_index()))
1273 seek_to_target_frame = replayer.get_frame_index() - 1;
1274 take_snapshot_at_frame_index = -1;
1278 vogl_printf("Resetting state and rewinding back to frame 0\n");
1280 replayer.reset_state();
1282 if (!pTrace_reader->seek_to_frame(0))
1284 vogl_error_printf("%s: Failed rewinding trace reader!\n", VOGL_FUNCTION_NAME);
1288 take_snapshot_at_frame_index = -1;
1289 paused_mode_frame_index = -1;
1290 paused_mode = false;
1293 vogl_delete(pSnapshot);
1296 else if (replayer.get_at_frame_boundary() && (!replayer.get_pending_apply_snapshot()))
1298 // Rewind to beginning
1299 if (keys_pressed.contains('r'))
1301 bool ctrl = (keys_down.contains(XK_Control_L) || keys_down.contains(XK_Control_R));
1302 keys_pressed.erase('r');
1304 vogl_delete(pSnapshot);
1307 if ((paused_mode) && (ctrl))
1308 seek_to_target_frame = paused_mode_frame_index;
1310 take_snapshot_at_frame_index = -1;
1311 paused_mode_frame_index = -1;
1312 paused_mode = false;
1314 vogl_printf("Resetting state and rewinding back to frame 0\n");
1316 replayer.reset_state();
1318 if (!pTrace_reader->seek_to_frame(0))
1320 vogl_error_printf("%s: Failed rewinding trace reader!\n", VOGL_FUNCTION_NAME);
1324 // Seek to last frame
1325 else if (keys_pressed.contains('e'))
1327 keys_pressed.erase('e');
1331 vogl_delete(pSnapshot);
1334 paused_mode_frame_index = -1;
1335 take_snapshot_at_frame_index = -1;
1337 vogl_printf("Seeking to last frame\n");
1339 int64_t max_frame_index = pTrace_reader->get_max_frame_index();
1340 if (max_frame_index < 0)
1342 vogl_error_printf("%s: Failed determining the total number of trace frames!\n", VOGL_FUNCTION_NAME);
1346 // "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
1347 seek_to_target_frame = max_frame_index - 1;
1350 // Check if paused and process seek related keypresses
1351 else if ((paused_mode) && (pSnapshot))
1354 for (num_key = '0'; num_key <= '9'; num_key++)
1356 if (keys_pressed.contains(num_key))
1358 keys_pressed.erase(num_key);
1365 int64_t max_frame_index = pTrace_reader->get_max_frame_index();
1366 if (max_frame_index < 0)
1368 vogl_error_printf("%s: Failed determining the total number of trace frames!\n", VOGL_FUNCTION_NAME);
1372 float fraction = ((num_key - '0') + 1) / 11.0f;
1374 seek_to_target_frame = math::clamp<int64_t>(static_cast<int64_t>(max_frame_index * fraction + .5f), 0, max_frame_index - 1);
1375 seek_to_closest_keyframe = true;
1377 else if (keys_pressed.contains(XK_Left) || keys_pressed.contains(XK_Right))
1379 int dir = keys_pressed.contains(XK_Left) ? -1 : 1;
1381 bool shift = (keys_down.contains(XK_Shift_L) || keys_down.contains(XK_Shift_R));
1382 bool ctrl = (keys_down.contains(XK_Control_L) || keys_down.contains(XK_Control_R));
1385 if ((shift) && (ctrl))
1392 int rel = dir * mag;
1394 int64_t target_frame_index = math::maximum<int64_t>(0, paused_mode_frame_index + rel);
1396 seek_to_target_frame = target_frame_index;
1398 keys_pressed.erase(XK_Left);
1399 keys_pressed.erase(XK_Right);
1401 if ((keyframes.size()) && (keys_down.contains(XK_Alt_L) || keys_down.contains(XK_Alt_R)))
1403 uint keyframe_array_index = 0;
1404 for (keyframe_array_index = 1; keyframe_array_index < keyframes.size(); keyframe_array_index++)
1405 if ((int64_t)keyframes[keyframe_array_index] > paused_mode_frame_index)
1410 if ((paused_mode_frame_index == static_cast<int64_t>(keyframes[keyframe_array_index - 1])) && (keyframe_array_index > 1))
1411 keyframe_array_index = keyframe_array_index - 2;
1413 keyframe_array_index = keyframe_array_index - 1;
1417 if (keyframe_array_index < keyframes.size())
1419 if ((paused_mode_frame_index == static_cast<int64_t>(keyframes[keyframe_array_index])) && ((keyframe_array_index + 1) < keyframes.size()))
1420 keyframe_array_index = keyframe_array_index + 1;
1422 // keyframe_array_index = keyframe_array_index;
1425 keyframe_array_index = keyframe_array_index - 1;
1428 seek_to_target_frame = keyframes[keyframe_array_index];
1432 if (((dir < 0) && (target_frame_index < seek_to_target_frame)) || (target_frame_index > seek_to_target_frame))
1433 seek_to_target_frame = target_frame_index;
1435 seek_to_closest_keyframe = true;
1436 seek_to_closest_frame_dir_bias = dir;
1439 console::info("Seeking to keyframe array index %u, target frame %" PRIu64 "\n", keyframe_array_index, seek_to_target_frame);
1442 // Check for unpause
1443 else if (keys_pressed.contains(XK_space))
1445 console::info("Unpausing\n");
1447 keys_pressed.erase(XK_space);
1449 vogl_delete(pSnapshot);
1452 paused_mode_frame_index = -1;
1454 paused_mode = false;
1458 // 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
1459 status = replayer.begin_applying_snapshot(pSnapshot, false);
1460 if ((status != vogl_gl_replayer::cStatusOK) && (status != vogl_gl_replayer::cStatusResizeWindow))
1462 vogl_error_printf("%s: Failed applying snapshot!\n", VOGL_FUNCTION_NAME);
1466 pTrace_reader->seek_to_frame(static_cast<uint>(paused_mode_frame_index));
1471 // Seek to target frame
1472 if (seek_to_target_frame != -1)
1474 vogl_delete(pSnapshot);
1476 paused_mode_frame_index = -1;
1478 if ((int64_t)replayer.get_frame_index() == seek_to_target_frame)
1479 take_snapshot_at_frame_index = seek_to_target_frame;
1482 uint keyframe_array_index = 0;
1483 for (keyframe_array_index = 1; keyframe_array_index < keyframes.size(); keyframe_array_index++)
1484 if (static_cast<int64_t>(keyframes[keyframe_array_index]) > seek_to_target_frame)
1487 if ((!keyframes.is_empty()) && (static_cast<int64_t>(keyframes[keyframe_array_index - 1]) <= seek_to_target_frame))
1489 int keyframe_array_index_to_use = keyframe_array_index - 1;
1491 if (seek_to_closest_keyframe)
1493 if (!seek_to_closest_frame_dir_bias)
1495 if ((keyframe_array_index_to_use + 1U) < keyframes.size())
1497 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))
1499 keyframe_array_index_to_use++;
1503 else if (seek_to_closest_frame_dir_bias > 0)
1505 if ((keyframe_array_index_to_use + 1U) < keyframes.size())
1507 if (static_cast<int64_t>(keyframes[keyframe_array_index_to_use]) <= seek_to_target_frame)
1509 keyframe_array_index_to_use++;
1515 int64_t keyframe_index = keyframes[keyframe_array_index_to_use];
1517 if (seek_to_closest_keyframe)
1518 seek_to_target_frame = keyframe_index;
1520 vogl_debug_printf("Seeking to target frame %" PRIu64 "\n", seek_to_target_frame);
1522 dynamic_string keyframe_filename(cVarArg, "%s_%06" PRIu64 ".bin", keyframe_base_filename.get_ptr(), keyframe_index);
1524 vogl_gl_state_snapshot *pKeyframe_snapshot = read_state_snapshot_from_trace(keyframe_filename);
1525 if (!pKeyframe_snapshot)
1528 bool delete_snapshot_after_applying = true;
1529 if (seek_to_target_frame == keyframe_index)
1530 delete_snapshot_after_applying = false;
1532 status = replayer.begin_applying_snapshot(pKeyframe_snapshot, delete_snapshot_after_applying);
1533 if ((status != vogl_gl_replayer::cStatusOK) && (status != vogl_gl_replayer::cStatusResizeWindow))
1535 vogl_error_printf("%s: Failed applying snapshot!\n", VOGL_FUNCTION_NAME);
1539 pKeyframe_snapshot->set_frame_index(static_cast<uint>(keyframe_index));
1541 if (!pTrace_reader->seek_to_frame(static_cast<uint>(keyframe_index)))
1543 vogl_error_printf("%s: Failed seeking to keyframe!\n", VOGL_FUNCTION_NAME);
1547 if (seek_to_target_frame == keyframe_index)
1549 pSnapshot = pKeyframe_snapshot;
1550 paused_mode_frame_index = seek_to_target_frame;
1553 take_snapshot_at_frame_index = seek_to_target_frame;
1557 if (seek_to_target_frame < static_cast<int64_t>(replayer.get_frame_index()))
1559 replayer.reset_state();
1560 pTrace_reader->seek_to_frame(0);
1563 take_snapshot_at_frame_index = seek_to_target_frame;
1568 else // !interactive_mode
1570 tmZone(TELEMETRY_LEVEL0, TMZF_NONE, "!Interactive");
1572 if (replayer.get_at_frame_boundary())
1574 if (trim_frames.size())
1576 bool should_trim = false;
1580 for (tf = 0; tf < trim_frames.size(); tf++)
1582 if (trim_lens.size())
1584 if (trim_lens.size() < trim_frames.size())
1587 len = trim_lens[tf];
1589 len = math::maximum(len, 1U);
1593 if ((replayer.get_frame_index() >= trim_frames[tf]) && (replayer.get_frame_index() < (trim_frames[tf] + math::maximum(len, 1U))))
1601 if (replayer.get_frame_index() == trim_frames[tf])
1613 if (multitrim_frames_remaining)
1615 should_trim = false;
1619 multitrim_frames_remaining = multitrim_interval;
1622 multitrim_frames_remaining--;
1626 multitrim_frames_remaining = 0;
1629 //printf("multitrim_interval: %u %u\n", multitrim_frames_remaining, multitrim_interval);
1633 dynamic_string filename;
1635 if ((multitrim_mode) || (trim_filenames.size() < trim_frames.size()))
1637 filename = trim_filenames[0];
1639 if ((multitrim_mode) || (trim_frames.size() > 1))
1641 dynamic_string drive, dir, fname, ext;
1642 file_utils::split_path(filename.get_ptr(), &drive, &dir, &fname, &ext);
1644 dynamic_string new_fname(cVarArg, "%s_%06u", fname.get_ptr(), replayer.get_frame_index());
1646 file_utils::combine_path_and_extension(filename, &drive, &dir, &new_fname, &ext);
1651 filename = trim_filenames[tf];
1654 dynamic_string trim_path(file_utils::get_pathname(filename.get_ptr()));
1655 trim_file_blob_manager.set_path(trim_path);
1657 file_utils::create_directories(trim_path, false);
1659 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);
1660 if (!replayer.write_trim_file(write_trim_file_flags, filename, multitrim_mode ? 1 : len, *pTrace_reader))
1663 num_trim_files_written++;
1665 if (!multitrim_mode)
1667 if (num_trim_files_written == trim_frames.size())
1669 vogl_message_printf("%s: All requested trim files written, stopping replay\n", VOGL_FUNCTION_NAME);
1677 uint64_t next_frame_index = replayer.get_frame_index() + 1;
1679 if (next_frame_index > highest_frame_to_trim)
1681 vogl_message_printf("%s: No more frames to trim, stopping replay\n", VOGL_FUNCTION_NAME);
1687 if ((!pSnapshot) && (loop_frame != -1) && (static_cast<int64_t>(replayer.get_frame_index()) == loop_frame))
1689 vogl_debug_printf("%s: Capturing replayer state at start of frame %u\n", VOGL_FUNCTION_NAME, replayer.get_frame_index());
1691 pSnapshot = replayer.snapshot_state();
1695 vogl_printf("Snapshot succeeded\n");
1697 snapshot_loop_start_frame = pTrace_reader->get_cur_frame();
1698 snapshot_loop_end_frame = pTrace_reader->get_cur_frame() + loop_len;
1700 if (draw_kill_max_thresh > 0)
1702 replayer.set_frame_draw_counter_kill_threshold(0);
1705 vogl_debug_printf("%s: Loop start: %" PRIi64 " Loop end: %" PRIi64 "\n", VOGL_FUNCTION_NAME, snapshot_loop_start_frame, snapshot_loop_end_frame);
1709 vogl_error_printf("Snapshot failed!\n");
1716 vogl_gl_replayer::status_t status = replayer.process_pending_window_resize();
1717 if (status == vogl_gl_replayer::cStatusOK)
1721 status = replayer.process_next_packet(*pTrace_reader);
1723 if ((status != vogl_gl_replayer::cStatusHardFailure) && (status != vogl_gl_replayer::cStatusAtEOF))
1725 if ((write_snapshot_index >= 0) && (write_snapshot_index == replayer.get_last_processed_call_counter()))
1727 dynamic_string filename(write_snapshot_filename);
1729 dynamic_string write_snapshot_path(file_utils::get_pathname(filename.get_ptr()));
1730 trim_file_blob_manager.init(cBMFReadWrite, write_snapshot_path.get_ptr());
1732 file_utils::create_directories(write_snapshot_path, false);
1734 pSnapshot = replayer.snapshot_state();
1738 vogl_printf("Snapshot succeeded at call counter %" PRIu64 "\n", replayer.get_last_processed_call_counter());
1740 vogl_null_blob_manager null_blob_manager;
1741 null_blob_manager.init(cBMFReadWrite);
1744 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);
1745 if (!pSnapshot->serialize(*doc.get_root(), *pBlob_manager, &replayer.get_trace_gl_ctypes()))
1747 vogl_error_printf("Failed serializing state snapshot document!\n");
1749 else if (!doc.serialize_to_file(filename.get_ptr(), true))
1751 vogl_error_printf("Failed writing state snapshot to file \"%s\"!\n", filename.get_ptr());
1755 vogl_printf("Successfully wrote JSON snapshot to file \"%s\"\n", filename.get_ptr());
1758 vogl_delete(pSnapshot);
1763 vogl_error_printf("Snapshot failed!\n");
1768 else if ((trim_call_index >= 0) && (trim_call_index == replayer.get_last_processed_call_counter()))
1770 dynamic_string filename(trim_filenames[0]);
1772 dynamic_string trim_path(file_utils::get_pathname(filename.get_ptr()));
1773 trim_file_blob_manager.set_path(trim_path);
1775 file_utils::create_directories(trim_path, false);
1777 if (!replayer.write_trim_file(0, filename, trim_lens.size() ? trim_lens[0] : 1, *pTrace_reader, NULL))
1780 vogl_message_printf("%s: Trim file written, stopping replay\n", VOGL_FUNCTION_NAME);
1785 if ((status == vogl_gl_replayer::cStatusNextFrame) || (status == vogl_gl_replayer::cStatusResizeWindow) || (status == vogl_gl_replayer::cStatusAtEOF) || (status == vogl_gl_replayer::cStatusHardFailure))
1790 if (status == vogl_gl_replayer::cStatusHardFailure)
1793 if (status == vogl_gl_replayer::cStatusAtEOF)
1795 vogl_message_printf("%s: At trace EOF, frame index %u\n", VOGL_FUNCTION_NAME, replayer.get_frame_index());
1798 if ((replayer.get_at_frame_boundary()) && (pSnapshot) && (loop_count > 0) && ((pTrace_reader->get_cur_frame() == snapshot_loop_end_frame) || (status == vogl_gl_replayer::cStatusAtEOF)))
1800 status = replayer.begin_applying_snapshot(pSnapshot, false);
1801 if ((status != vogl_gl_replayer::cStatusOK) && (status != vogl_gl_replayer::cStatusResizeWindow))
1804 pTrace_reader->seek_to_frame(static_cast<uint>(snapshot_loop_start_frame));
1806 if (draw_kill_max_thresh > 0)
1808 int64_t thresh = replayer.get_frame_draw_counter_kill_threshold();
1810 if (thresh >= draw_kill_max_thresh)
1812 replayer.set_frame_draw_counter_kill_threshold(thresh);
1814 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);
1817 vogl_debug_printf("%s: Applying snapshot and seeking back to frame %" PRIi64 "\n", VOGL_FUNCTION_NAME, snapshot_loop_start_frame);
1823 bool print_progress = (status == vogl_gl_replayer::cStatusAtEOF) || ((replayer.get_at_frame_boundary()) && ((replayer.get_frame_index() % 100) == 0));
1826 if (pTrace_reader->get_type() == cBINARY_TRACE_FILE_READER)
1828 vogl_binary_trace_file_reader &binary_trace_reader = *static_cast<vogl_binary_trace_file_reader *>(pTrace_reader.get());
1830 vogl_printf("Replay now at frame index %d, trace file offet %" PRIu64 ", GL call counter %" PRIu64 ", %3.2f%% percent complete\n",
1831 replayer.get_frame_index(),
1832 binary_trace_reader.get_cur_file_ofs(),
1833 replayer.get_last_parsed_call_counter(),
1834 binary_trace_reader.get_trace_file_size() ? (binary_trace_reader.get_cur_file_ofs() * 100.0f) / binary_trace_reader.get_trace_file_size() : 0);
1838 if (status == vogl_gl_replayer::cStatusAtEOF)
1842 double time_since_start = tm.get_elapsed_secs();
1844 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);
1849 vogl_printf("Resetting state and rewinding back to frame 0\n");
1851 replayer.reset_state();
1853 if (!pTrace_reader->seek_to_frame(0))
1855 vogl_error_printf("%s: Failed rewinding trace reader!\n", VOGL_FUNCTION_NAME);
1860 } // interactive_mode
1865 if (trim_frames.size())
1867 console::message("Wrote %u trim file(s)\n", num_trim_files_written);
1869 if (((multitrim_mode) && (!num_trim_files_written)) ||
1870 ((!multitrim_mode) && (num_trim_files_written != trim_frames.size())))
1871 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);
1876 if (g_command_line_params.get_value_as_bool("pause_on_exit") && (window.is_opened()))
1878 vogl_printf("Press a key to continue.\n");
1885 bool exit_flag = false;
1886 while (!exit_flag && X11_Pending(window.get_display()))
1889 XNextEvent(window.get_display(), &newEvent);
1891 switch (newEvent.type)
1901 if (newEvent.xclient.data.l[0] == (int)wmDeleteMessage)
1921 //----------------------------------------------------------------------------------------------------------------------
1923 //----------------------------------------------------------------------------------------------------------------------
1924 static bool tool_dump_mode()
1928 dynamic_string input_trace_filename(g_command_line_params.get_value_as_string_or_empty("", 1));
1929 if (input_trace_filename.is_empty())
1931 vogl_error_printf("Must specify filename of input binary trace file!\n");
1935 dynamic_string output_base_filename(g_command_line_params.get_value_as_string_or_empty("", 2));
1936 if (output_base_filename.is_empty())
1938 vogl_error_printf("Must specify base filename of output JSON/blob files!\n");
1942 vogl_loose_file_blob_manager output_file_blob_manager;
1944 dynamic_string output_trace_path(file_utils::get_pathname(output_base_filename.get_ptr()));
1945 vogl_debug_printf("%s: Output trace path: %s\n", VOGL_FUNCTION_NAME, output_trace_path.get_ptr());
1946 output_file_blob_manager.init(cBMFReadWrite, output_trace_path.get_ptr());
1948 file_utils::create_directories(output_trace_path, false);
1950 dynamic_string actual_input_trace_filename;
1951 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()));
1952 if (!pTrace_reader.get())
1954 vogl_error_printf("%s: Failed opening input trace file \"%s\"\n", VOGL_FUNCTION_NAME, input_trace_filename.get_ptr());
1958 const bool full_verification = g_command_line_params.get_value_as_bool("verify");
1960 vogl_ctypes trace_gl_ctypes;
1961 trace_gl_ctypes.init(pTrace_reader->get_sof_packet().m_pointer_sizes);
1963 dynamic_string archive_name;
1964 if (pTrace_reader->get_archive_blob_manager().is_initialized())
1966 dynamic_string archive_filename(output_base_filename.get_ptr());
1967 archive_filename += "_trace_archive.zip";
1969 archive_name = file_utils::get_filename(archive_filename.get_ptr());
1971 vogl_message_printf("Writing trace archive \"%s\", size %" PRIu64 " bytes\n", archive_filename.get_ptr(), pTrace_reader->get_archive_blob_manager().get_archive_size());
1973 cfile_stream archive_stream;
1974 if (!archive_stream.open(archive_filename.get_ptr(), cDataStreamWritable | cDataStreamSeekable))
1976 vogl_error_printf("%s: Failed opening output trace archive \"%s\"!\n", VOGL_FUNCTION_NAME, archive_filename.get_ptr());
1980 if (!pTrace_reader->get_archive_blob_manager().write_archive_to_stream(archive_stream))
1982 vogl_error_printf("%s: Failed writing to output trace archive \"%s\"!\n", VOGL_FUNCTION_NAME, archive_filename.get_ptr());
1986 if (!archive_stream.close())
1988 vogl_error_printf("%s: Failed writing to output trace archive \"%s\"!\n", VOGL_FUNCTION_NAME, archive_filename.get_ptr());
1993 vogl_trace_packet gl_packet_cracker(&trace_gl_ctypes);
1995 uint cur_file_index = 0;
1996 uint cur_frame_index = 0;
1997 uint64_t cur_packet_index = 0;
1998 VOGL_NOTE_UNUSED(cur_packet_index);
1999 json_document cur_doc;
2002 json_node &meta_node = cur_doc.get_root()->add_object("meta");
2003 meta_node.add_key_value("cur_frame", cur_frame_index);
2005 json_node &sof_node = cur_doc.get_root()->add_object("sof");
2006 sof_node.add_key_value("pointer_sizes", pTrace_reader->get_sof_packet().m_pointer_sizes);
2007 sof_node.add_key_value("version", to_hex_string(pTrace_reader->get_sof_packet().m_version));
2008 if (!archive_name.is_empty())
2009 sof_node.add_key_value("archive_filename", archive_name);
2011 json_node &uuid_array = sof_node.add_array("uuid");
2012 for (uint i = 0; i < VOGL_ARRAY_SIZE(pTrace_reader->get_sof_packet().m_uuid); i++)
2013 uuid_array.add_value(pTrace_reader->get_sof_packet().m_uuid[i]);
2016 // TODO: Automatically dump binary snapshot file to text?
2017 // Right now we can't afford to do it at trace time, it takes too much memory.
2019 json_node *pPacket_array = &cur_doc.get_root()->add_array("packets");
2021 bool flush_current_document = false;
2027 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;
2029 vogl_trace_file_reader::trace_file_reader_status_t read_status = pTrace_reader->read_next_packet();
2031 if (read_status == vogl_trace_file_reader::cEOF)
2033 vogl_message_printf("At trace file EOF\n");
2036 else if (read_status != vogl_trace_file_reader::cOK)
2038 vogl_error_printf("Failed reading from trace file, or file size was too small\n");
2044 if (pTrace_reader->get_packet_type() != cTSPTGLEntrypoint)
2046 if (pTrace_reader->get_packet_type() == cTSPTSOF)
2048 vogl_error_printf("Encountered redundant SOF packet!\n");
2052 json_node *pMeta_node = cur_doc.get_root()->find_child_object("meta");
2054 pMeta_node->add_key_value("eof", (pTrace_reader->get_packet_type() == cTSPTEOF) ? 1 : 2);
2059 if (flush_current_document)
2061 flush_current_document = false;
2063 dynamic_string output_filename(cVarArg, "%s_%06u.json", output_base_filename.get_ptr(), cur_file_index);
2064 vogl_message_printf("Writing file: \"%s\"\n", output_filename.get_ptr());
2066 if (!cur_doc.serialize_to_file(output_filename.get_ptr(), true))
2068 vogl_error_printf("%s: Failed serializing JSON document to file %s\n", VOGL_FUNCTION_NAME, output_filename.get_ptr());
2074 if (cur_doc.get_root()->check_for_duplicate_keys())
2075 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());
2079 pPacket_array = NULL;
2082 json_node ¬e_node = cur_doc.get_root()->add_object("meta");
2083 note_node.add_key_value("cur_frame", cur_frame_index);
2085 json_node &uuid_array = note_node.add_array("uuid");
2086 for (uint i = 0; i < VOGL_ARRAY_SIZE(pTrace_reader->get_sof_packet().m_uuid); i++)
2087 uuid_array.add_value(pTrace_reader->get_sof_packet().m_uuid[i]);
2089 pPacket_array = &cur_doc.get_root()->add_array("packets");
2092 const vogl_trace_gl_entrypoint_packet &gl_packet = pTrace_reader->get_packet<vogl_trace_gl_entrypoint_packet>();
2093 const char *pFunc_name = g_vogl_entrypoint_descs[gl_packet.m_entrypoint_id].m_pName;
2094 VOGL_NOTE_UNUSED(pFunc_name);
2096 if (g_command_line_params.get_value_as_bool("debug"))
2098 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",
2101 gl_packet.m_param_size,
2102 gl_packet.m_client_memory_size,
2103 gl_packet.m_name_value_map_size,
2104 gl_packet.m_call_counter,
2105 g_vogl_entrypoint_descs[gl_packet.m_entrypoint_id].m_pName,
2106 gl_packet.m_entrypoint_id,
2107 gl_packet.m_thread_id,
2108 gl_packet.m_context_handle);
2111 if (!gl_packet_cracker.deserialize(pTrace_reader->get_packet_buf().get_ptr(), pTrace_reader->get_packet_buf().size(), true))
2113 vogl_error_printf("Failed deserializing GL entrypoint packet. Trying to continue parsing the file, this may die!\n");
2120 json_node &new_node = pPacket_array->add_object();
2122 vogl_trace_packet::json_serialize_params serialize_params;
2123 serialize_params.m_output_basename = file_utils::get_filename(output_base_filename.get_ptr());
2124 serialize_params.m_pBlob_manager = &output_file_blob_manager;
2125 serialize_params.m_cur_frame = cur_file_index;
2126 serialize_params.m_write_debug_info = g_command_line_params.get_value_as_bool("write_debug_info");
2127 if (!gl_packet_cracker.json_serialize(new_node, serialize_params))
2129 vogl_error_printf("JSON serialization failed!\n");
2135 if (full_verification)
2138 if (!strcmp(pFunc_name, "glClearColor"))
2144 vogl::vector<char> new_node_as_text;
2145 new_node.serialize(new_node_as_text, true, 0);
2148 if (new_node_as_text.size())
2150 printf("%s\n", new_node_as_text.get_ptr());
2154 json_document round_tripped_node;
2155 if (!round_tripped_node.deserialize(new_node_as_text.get_ptr()) || !round_tripped_node.get_root())
2157 vogl_error_printf("Failed verifying serialized JSON data (step 1)!\n");
2164 vogl_trace_packet temp_cracker(&trace_gl_ctypes);
2165 bool success = temp_cracker.json_deserialize(*round_tripped_node.get_root(), "<memory>", &output_file_blob_manager);
2168 vogl_error_printf("Failed verifying serialized JSON data (step 2)!\n");
2175 success = gl_packet_cracker.compare(temp_cracker, false);
2178 vogl_error_printf("Failed verifying serialized JSON data (step 3)!\n");
2185 dynamic_stream dyn_stream;
2187 success = temp_cracker.serialize(dyn_stream);
2190 vogl_error_printf("Failed verifying serialized JSON data (step 4)!\n");
2197 vogl_trace_packet temp_cracker2(&trace_gl_ctypes);
2198 success = temp_cracker2.deserialize(static_cast<const uint8 *>(dyn_stream.get_ptr()), static_cast<uint32>(dyn_stream.get_size()), true);
2201 vogl_error_printf("Failed verifying serialized JSON data (step 5)!\n");
2206 success = gl_packet_cracker.compare(temp_cracker2, true);
2209 vogl_error_printf("Failed verifying serialized JSON data (step 6)!\n");
2216 uint64_t binary_serialized_size = dyn_stream.get_size();
2217 if (binary_serialized_size != pTrace_reader->get_packet_buf().size())
2219 vogl_error_printf("Round-tripped binary serialized size differs from original packet's' size (step 7)!\n");
2227 // This is excessive- the key value map fields may be binary serialized in different orders
2228 // TODO: maybe fix the key value map class so it serializes in a stable order (independent of hash table construction)?
2229 const uint8 *p = static_cast<const uint8 *>(dyn_stream.get_ptr());
2230 const uint8 *q = static_cast<const uint8 *>(pTrace_reader->get_packet_buf().get_ptr());
2231 if (memcmp(p, q, binary_serialized_size) != 0)
2233 file_utils::write_buf_to_file("p.bin", p, binary_serialized_size);
2234 file_utils::write_buf_to_file("q.bin", q, binary_serialized_size);
2235 vogl_error_printf("Round-tripped binary serialized data differs from original packet's data (step 7)!\n");
2249 if (vogl_is_swap_buffers_entrypoint(static_cast<gl_entrypoint_id_t>(gl_packet.m_entrypoint_id)))
2251 flush_current_document = true;
2255 if (cur_doc.get_root()->size() >= 1 * 1000 * 1000)
2257 // TODO: Support replaying dumps like this, or fix the code to serialize the text as it goes.
2258 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");
2259 flush_current_document = true;
2263 json_node *pMeta_node = cur_doc.get_root()->find_child_object("meta");
2267 if (!pMeta_node->has_key("eof"))
2268 pMeta_node->add_key_value("eof", 2);
2270 dynamic_string output_filename(cVarArg, "%s_%06u.json", output_base_filename.get_ptr(), cur_file_index);
2271 vogl_message_printf("Writing file: \"%s\"\n", output_filename.get_ptr());
2273 if (!cur_doc.serialize_to_file(output_filename.get_ptr(), true))
2275 vogl_error_printf("%s: Failed serializing JSON document to file %s\n", VOGL_FUNCTION_NAME, output_filename.get_ptr());
2280 if (cur_doc.get_root()->check_for_duplicate_keys())
2281 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());
2287 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());
2289 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());
2294 //----------------------------------------------------------------------------------------------------------------------
2296 //----------------------------------------------------------------------------------------------------------------------
2297 static bool tool_parse_mode()
2301 dynamic_string input_base_filename(g_command_line_params.get_value_as_string_or_empty("", 1));
2302 if (input_base_filename.is_empty())
2304 vogl_error_printf("Must specify filename of input JSON/blob trace files!\n");
2308 dynamic_string actual_input_filename;
2309 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()));
2310 if (!pTrace_reader.get())
2313 dynamic_string output_trace_filename(g_command_line_params.get_value_as_string_or_empty("", 2));
2314 if (output_trace_filename.is_empty())
2316 vogl_error_printf("Must specify full filename of output binary trace file!\n");
2320 file_utils::create_directories(output_trace_filename, true);
2322 if (file_utils::add_default_extension(output_trace_filename, ".bin"))
2323 vogl_message_printf("Trim output filename doesn't have an extension, appending \".bin\" to the filename\n");
2325 // TODO: Refactor this, I think we're going to write the actual ctypes and entrpoint descs, etc. into the trace archive next.
2326 vogl_ctypes trace_ctypes;
2327 trace_ctypes.init(pTrace_reader->get_sof_packet().m_pointer_sizes);
2329 vogl_trace_file_writer trace_writer(&trace_ctypes);
2330 if (!trace_writer.open(output_trace_filename.get_ptr(), NULL, true, false, pTrace_reader->get_sof_packet().m_pointer_sizes))
2332 vogl_error_printf("Unable to create file \"%s\"!\n", output_trace_filename.get_ptr());
2336 if (pTrace_reader->get_archive_blob_manager().is_initialized())
2338 dynamic_string_array blob_files(pTrace_reader->get_archive_blob_manager().enumerate());
2339 for (uint i = 0; i < blob_files.size(); i++)
2341 if (blob_files[i] == VOGL_TRACE_ARCHIVE_FRAME_FILE_OFFSETS_FILENAME)
2344 vogl_message_printf("Adding blob file %s to output trace archive\n", blob_files[i].get_ptr());
2346 uint8_vec blob_data;
2347 if (pTrace_reader->get_archive_blob_manager().get(blob_files[i], blob_data))
2349 if (trace_writer.get_trace_archive()->add_buf_using_id(blob_data.get_ptr(), blob_data.size(), blob_files[i]).is_empty())
2351 vogl_error_printf("%s: Failed writing blob data %s to output trace archive!\n", VOGL_FUNCTION_NAME, blob_files[i].get_ptr());
2357 vogl_error_printf("%s: Failed reading blob data %s from trace archive!\n", VOGL_FUNCTION_NAME, blob_files[i].get_ptr());
2365 vogl_trace_file_reader::trace_file_reader_status_t read_status = pTrace_reader->read_next_packet();
2367 if ((read_status != vogl_trace_file_reader::cOK) && (read_status != vogl_trace_file_reader::cEOF))
2369 vogl_error_printf("Failed reading from trace file\n");
2373 if ((read_status == vogl_trace_file_reader::cEOF) || (pTrace_reader->is_eof_packet()))
2375 vogl_message_printf("At trace file EOF\n");
2379 const vogl::vector<uint8> &packet_buf = pTrace_reader->get_packet_buf();
2381 if (!trace_writer.write_packet(packet_buf.get_ptr(), packet_buf.size(), pTrace_reader->is_swap_buffers_packet()))
2383 vogl_error_printf("Failed writing to output trace file \"%s\"\n", output_trace_filename.get_ptr());
2388 if (!trace_writer.close())
2390 vogl_error_printf("Failed closing output trace file \"%s\"\n", output_trace_filename.get_ptr());
2394 vogl_message_printf("Successfully wrote binary trace file \"%s\"\n", output_trace_filename.get_ptr());
2399 trace_writer.close();
2401 vogl_warning_printf("Processing failed, output trace file \"%s\" may be invalid!\n", output_trace_filename.get_ptr());
2406 //----------------------------------------------------------------------------------------------------------------------
2407 // struct histo_entry
2408 //----------------------------------------------------------------------------------------------------------------------
2409 typedef vogl::map<dynamic_string> dynamic_string_set;
2410 typedef vogl::map<dynamic_string, uint64_t> dynamic_string_hash_map;
2411 typedef vogl::map<uint> uint_map;
2418 histo_entry(dynamic_string_hash_map::const_iterator it, double val)
2419 : m_it(it), m_value(val)
2423 dynamic_string_hash_map::const_iterator m_it;
2426 bool operator<(const histo_entry &rhs) const
2428 return m_value > rhs.m_value;
2432 //----------------------------------------------------------------------------------------------------------------------
2434 //----------------------------------------------------------------------------------------------------------------------
2435 #define SAFE_FLOAT_DIV(n, d) (d ? ((double)(n) / (d)) : 0)
2437 static void dump_histogram(const char *pMsg, const dynamic_string_hash_map &map, uint64_t total_gl_entrypoint_packets, uint64_t total_swaps)
2441 vogl::vector<histo_entry> histo;
2442 for (dynamic_string_hash_map::const_iterator it = map.begin(); it != map.end(); ++it)
2443 histo.push_back(histo_entry(it, static_cast<double>(it->second)));
2445 vogl_printf("\n----------------------\n%s: %u\n", pMsg, map.size());
2447 for (uint i = 0; i < histo.size(); i++)
2449 dynamic_string_hash_map::const_iterator it = histo[i].m_it;
2450 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));
2454 //----------------------------------------------------------------------------------------------------------------------
2456 //----------------------------------------------------------------------------------------------------------------------
2457 static bool tool_info_mode()
2461 dynamic_string input_base_filename(g_command_line_params.get_value_as_string_or_empty("", 1));
2462 if (input_base_filename.is_empty())
2464 vogl_error_printf("Must specify filename of input JSON/blob trace files!\n");
2468 dynamic_string actual_input_filename;
2469 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()));
2470 if (!pTrace_reader.get())
2473 vogl_printf("Scanning trace file %s\n", actual_input_filename.get_ptr());
2475 const vogl_trace_stream_start_of_file_packet &sof_packet = pTrace_reader->get_sof_packet();
2477 if (pTrace_reader->get_type() == cBINARY_TRACE_FILE_READER)
2479 uint64_t file_size = dynamic_cast<vogl_binary_trace_file_reader *>(pTrace_reader.get())->get_stream().get_size();
2480 vogl_printf("Total file size: %s\n", uint64_to_string_with_commas(file_size).get_ptr());
2483 vogl_printf("SOF packet size: %" PRIu64 " bytes\n", sof_packet.m_size);
2484 vogl_printf("Version: 0x%04X\n", sof_packet.m_version);
2485 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]);
2486 vogl_printf("First packet offset: %" PRIu64 "\n", sof_packet.m_first_packet_offset);
2487 vogl_printf("Trace pointer size: %u\n", sof_packet.m_pointer_sizes);
2488 vogl_printf("Trace archive size: %" PRIu64 " offset: %" PRIu64 "\n", sof_packet.m_archive_size, sof_packet.m_archive_offset);
2489 vogl_printf("Can quickly seek forward: %u\nMax frame index: %" PRIu64 "\n", pTrace_reader->can_quickly_seek_forward(), pTrace_reader->get_max_frame_index());
2491 if (!pTrace_reader->get_archive_blob_manager().is_initialized())
2493 vogl_warning_printf("This trace does not have a trace archive!\n");
2497 vogl_printf("----------------------\n");
2498 vogl::vector<dynamic_string> archive_files(pTrace_reader->get_archive_blob_manager().enumerate());
2499 vogl_printf("Total trace archive files: %u\n", archive_files.size());
2500 for (uint i = 0; i < archive_files.size(); i++)
2501 vogl_printf("\"%s\"\n", archive_files[i].get_ptr());
2502 vogl_printf("----------------------\n");
2505 uint min_packet_size = cUINT32_MAX;
2506 uint max_packet_size = 0;
2507 uint64_t total_packets = 1; // 1, not 0, to account for the SOF packet
2508 uint64_t total_packet_bytes = 0;
2509 uint64_t total_swaps = 0;
2510 uint64_t total_make_currents = 0;
2511 uint64_t total_internal_trace_commands = 0;
2512 uint64_t total_non_gl_entrypoint_packets = 1; // 1, not 0, to account for the SOF packet
2513 uint64_t total_gl_entrypoint_packets = 0;
2514 uint64_t total_gl_commands = 0;
2515 uint64_t total_glx_commands = 0;
2516 uint64_t total_wgl_commands = 0;
2517 uint64_t total_unknown_commands = 0;
2518 uint64_t total_draws = 0;
2519 bool found_eof_packet = false;
2521 uint64_t min_packets_per_frame = cUINT64_MAX;
2522 uint64_t max_packets_per_frame = 0;
2523 uint64_t max_frame_make_currents = 0;
2524 uint64_t min_frame_make_currents = cUINT64_MAX;
2526 uint64_t min_frame_draws = cUINT64_MAX;
2527 uint64_t max_frame_draws = 0;
2528 uint64_t cur_frame_draws = 0;
2530 uint64_t cur_frame_packet_count = 0;
2531 uint64_t total_frame_make_currents = 0;
2533 uint64_t num_non_whitelisted_funcs = 0;
2534 dynamic_string_set non_whitelisted_funcs_called;
2536 uint64_t total_programs_linked = 0;
2537 uint64_t total_program_binary_calls = 0;
2538 uint_map unique_programs_used;
2540 uint total_gl_state_snapshots = 0;
2542 uint total_display_list_calls = false;
2543 uint total_gl_get_errors = 0;
2545 uint total_context_creates = 0;
2546 uint total_context_destroys = 0;
2548 dynamic_string_hash_map all_apis_called, category_histogram, version_histogram, profile_histogram, deprecated_histogram;
2550 vogl_ctypes trace_gl_ctypes(pTrace_reader->get_sof_packet().m_pointer_sizes);
2552 vogl_trace_packet trace_packet(&trace_gl_ctypes);
2556 vogl_trace_file_reader::trace_file_reader_status_t read_status = pTrace_reader->read_next_packet();
2558 if ((read_status != vogl_trace_file_reader::cOK) && (read_status != vogl_trace_file_reader::cEOF))
2560 vogl_error_printf("Failed reading from trace file!\n");
2565 if (read_status == vogl_trace_file_reader::cEOF)
2567 vogl_printf("At trace file EOF on swap %" PRIu64 "\n", total_swaps);
2571 const vogl::vector<uint8> &packet_buf = pTrace_reader->get_packet_buf();
2572 VOGL_NOTE_UNUSED(packet_buf);
2574 uint packet_size = pTrace_reader->get_packet_size();
2576 min_packet_size = math::minimum<uint>(min_packet_size, packet_size);
2577 max_packet_size = math::maximum<uint>(max_packet_size, packet_size);
2579 total_packet_bytes += packet_size;
2581 cur_frame_packet_count++;
2583 const vogl_trace_stream_packet_base &base_packet = pTrace_reader->get_base_packet();
2584 VOGL_NOTE_UNUSED(base_packet);
2585 const vogl_trace_gl_entrypoint_packet *pGL_packet = NULL;
2587 if (pTrace_reader->get_packet_type() == cTSPTGLEntrypoint)
2589 if (!trace_packet.deserialize(pTrace_reader->get_packet_buf().get_ptr(), pTrace_reader->get_packet_buf().size(), false))
2591 console::error("%s: Failed parsing GL entrypoint packet\n", VOGL_FUNCTION_NAME);
2595 pGL_packet = &pTrace_reader->get_packet<vogl_trace_gl_entrypoint_packet>();
2596 gl_entrypoint_id_t entrypoint_id = static_cast<gl_entrypoint_id_t>(pGL_packet->m_entrypoint_id);
2598 const gl_entrypoint_desc_t &entrypoint_desc = g_vogl_entrypoint_descs[entrypoint_id];
2600 all_apis_called[entrypoint_desc.m_pName]++;
2601 category_histogram[entrypoint_desc.m_pCategory]++;
2602 version_histogram[entrypoint_desc.m_pVersion]++;
2603 profile_histogram[entrypoint_desc.m_pProfile]++;
2604 deprecated_histogram[entrypoint_desc.m_pDeprecated]++;
2606 if (!entrypoint_desc.m_is_whitelisted)
2608 num_non_whitelisted_funcs++;
2609 non_whitelisted_funcs_called.insert(entrypoint_desc.m_pName);
2612 if (!strcmp(entrypoint_desc.m_pAPI_prefix, "GLX"))
2613 total_glx_commands++;
2614 else if (!strcmp(entrypoint_desc.m_pAPI_prefix, "WGL"))
2615 total_wgl_commands++;
2616 else if (!strcmp(entrypoint_desc.m_pAPI_prefix, "GL"))
2617 total_gl_commands++;
2619 total_unknown_commands++;
2621 total_gl_entrypoint_packets++;
2623 if (vogl_is_swap_buffers_entrypoint(entrypoint_id))
2626 if ((total_swaps & 255) == 255)
2627 console::progress("Frame %" PRIu64 "\n", total_swaps);
2629 min_packets_per_frame = math::minimum(min_packets_per_frame, cur_frame_packet_count);
2630 max_packets_per_frame = math::maximum(max_packets_per_frame, cur_frame_packet_count);
2632 min_frame_draws = math::minimum(min_frame_draws, cur_frame_draws);
2633 max_frame_draws = math::maximum(max_frame_draws, cur_frame_draws);
2635 max_frame_make_currents = math::maximum(max_frame_make_currents, total_frame_make_currents);
2636 min_frame_make_currents = math::minimum(min_frame_make_currents, total_frame_make_currents);
2638 cur_frame_packet_count = 0;
2639 total_frame_make_currents = 0;
2640 cur_frame_draws = 0;
2642 else if (vogl_is_draw_entrypoint(entrypoint_id))
2647 else if (vogl_is_make_current_entrypoint(entrypoint_id))
2649 total_make_currents++;
2650 total_frame_make_currents++;
2653 switch (entrypoint_id)
2655 case VOGL_ENTRYPOINT_glInternalTraceCommandRAD:
2657 total_internal_trace_commands++;
2659 GLuint cmd = trace_packet.get_param_value<GLuint>(0);
2660 GLuint size = trace_packet.get_param_value<GLuint>(1);
2661 VOGL_NOTE_UNUSED(size);
2663 if (cmd == cITCRKeyValueMap)
2665 key_value_map &kvm = trace_packet.get_key_value_map();
2667 dynamic_string cmd_type(kvm.get_string("command_type"));
2668 if (cmd_type == "state_snapshot")
2670 total_gl_state_snapshots++;
2676 case VOGL_ENTRYPOINT_glProgramBinary:
2678 total_program_binary_calls++;
2681 case VOGL_ENTRYPOINT_glLinkProgram:
2682 case VOGL_ENTRYPOINT_glLinkProgramARB:
2684 total_programs_linked++;
2687 case VOGL_ENTRYPOINT_glUseProgram:
2688 case VOGL_ENTRYPOINT_glUseProgramObjectARB:
2690 GLuint trace_handle = trace_packet.get_param_value<GLuint>(0);
2691 unique_programs_used.insert(trace_handle);
2695 case VOGL_ENTRYPOINT_glGenLists:
2696 case VOGL_ENTRYPOINT_glDeleteLists:
2697 case VOGL_ENTRYPOINT_glXUseXFont:
2698 case VOGL_ENTRYPOINT_glCallList:
2699 case VOGL_ENTRYPOINT_glCallLists:
2700 case VOGL_ENTRYPOINT_glListBase:
2702 total_display_list_calls++;
2705 case VOGL_ENTRYPOINT_glGetError:
2707 total_gl_get_errors++;
2710 case VOGL_ENTRYPOINT_glXCreateContext:
2711 case VOGL_ENTRYPOINT_glXCreateContextAttribsARB:
2713 total_context_creates++;
2716 case VOGL_ENTRYPOINT_glXDestroyContext:
2718 total_context_destroys++;
2727 total_non_gl_entrypoint_packets++;
2730 if (pTrace_reader->get_packet_type() == cTSPTEOF)
2732 found_eof_packet = true;
2733 vogl_printf("Found trace file EOF packet on swap %" PRIu64 "\n", total_swaps);
2741 #define PRINT_UINT_VAR(x) vogl_printf("%s: %u\n", dynamic_string(#x).replace("_", " ").get_ptr(), x);
2742 #define PRINT_UINT64_VAR(x) vogl_printf("%s: %" PRIu64 "\n", dynamic_string(#x).replace("_", " ").get_ptr(), x);
2743 #define PRINT_FLOAT(x, f) vogl_printf("%s: %f\n", dynamic_string(#x).replace("_", " ").get_ptr(), f);
2745 PRINT_UINT64_VAR(num_non_whitelisted_funcs);
2746 PRINT_UINT_VAR(total_gl_state_snapshots);
2748 PRINT_UINT64_VAR(total_swaps);
2750 PRINT_UINT64_VAR(total_make_currents);
2751 PRINT_FLOAT(avg_make_currents_per_frame, SAFE_FLOAT_DIV(total_make_currents, total_swaps));
2752 PRINT_UINT64_VAR(max_frame_make_currents);
2753 PRINT_UINT64_VAR(min_frame_make_currents);
2755 PRINT_UINT64_VAR(total_draws);
2756 PRINT_FLOAT(avg_draws_per_frame, SAFE_FLOAT_DIV(total_draws, total_swaps));
2757 PRINT_UINT64_VAR(min_frame_draws);
2758 PRINT_UINT64_VAR(max_frame_draws);
2760 PRINT_UINT_VAR(min_packet_size);
2761 PRINT_UINT_VAR(max_packet_size);
2762 PRINT_UINT64_VAR(total_packets);
2763 PRINT_UINT64_VAR(total_packet_bytes);
2764 PRINT_FLOAT(avg_packet_bytes_per_frame, SAFE_FLOAT_DIV(total_packet_bytes, total_swaps));
2765 PRINT_FLOAT(avg_packet_size, SAFE_FLOAT_DIV(total_packet_bytes, total_packets));
2766 PRINT_UINT64_VAR(min_packets_per_frame);
2767 PRINT_UINT64_VAR(max_packets_per_frame);
2768 PRINT_FLOAT(avg_packets_per_frame, SAFE_FLOAT_DIV(total_packets, total_swaps));
2770 PRINT_UINT64_VAR(total_internal_trace_commands);
2771 PRINT_UINT64_VAR(total_non_gl_entrypoint_packets);
2772 PRINT_UINT64_VAR(total_gl_entrypoint_packets);
2773 PRINT_UINT64_VAR(total_gl_commands);
2774 PRINT_UINT64_VAR(total_glx_commands);
2775 PRINT_UINT64_VAR(total_wgl_commands);
2776 PRINT_UINT64_VAR(total_unknown_commands);
2778 PRINT_UINT_VAR(found_eof_packet);
2780 PRINT_UINT_VAR(total_display_list_calls);
2781 printf("Avg display lists calls per frame: %f\n", SAFE_FLOAT_DIV(total_display_list_calls, total_swaps));
2783 PRINT_UINT_VAR(total_gl_get_errors);
2784 printf("Avg glGetError calls per frame: %f\n", SAFE_FLOAT_DIV(total_gl_get_errors, total_swaps));
2786 PRINT_UINT_VAR(total_context_creates);
2787 PRINT_UINT_VAR(total_context_destroys);
2790 #undef PRINT_UINT64_VAR
2793 vogl_printf("----------------------\n%s: %" PRIu64 "\n", "Total calls to glLinkProgram/glLinkProgramARB", total_programs_linked);
2794 vogl_printf("%s: %" PRIu64 "\n", "Total calls to glProgramBinary", total_program_binary_calls);
2795 vogl_printf("%s: %u\n", "Total unique program handles passed to glUseProgram/glUseProgramObjectARB", unique_programs_used.size());
2797 dump_histogram("API histogram", all_apis_called, total_gl_entrypoint_packets, total_swaps);
2798 dump_histogram("API Category histogram", category_histogram, total_gl_entrypoint_packets, total_swaps);
2799 dump_histogram("API Version histogram", version_histogram, total_gl_entrypoint_packets, total_swaps);
2800 //dump_histogram("API Profile histogram", profile_histogram, total_gl_entrypoint_packets, total_swaps);
2801 //dump_histogram("API Deprecated histogram", deprecated_histogram, total_gl_entrypoint_packets, total_swaps);
2803 #undef DUMP_HISTOGRAM
2805 if (non_whitelisted_funcs_called.size())
2807 vogl_warning_printf("\n----------------------\n");
2808 vogl_warning_printf("Number of non-whitelisted functions actually called: %u\n", non_whitelisted_funcs_called.size());
2809 for (dynamic_string_set::const_iterator it = non_whitelisted_funcs_called.begin(); it != non_whitelisted_funcs_called.end(); ++it)
2810 vogl_warning_printf("%s\n", it->first.get_ptr());
2811 vogl_warning_printf("\n----------------------\n");
2816 #undef SAFE_FLOAT_DIV
2818 //----------------------------------------------------------------------------------------------------------------------
2819 // param_value_matches
2820 //----------------------------------------------------------------------------------------------------------------------
2821 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)
2823 if (find_namespace != VOGL_NAMESPACE_UNKNOWN)
2825 if (find_namespace != value_namespace)
2831 switch (value_ctype)
2834 case VOGL_GLBOOLEAN:
2848 // Not supporting float/double finds for now
2854 // Not supporting float/double finds for now
2863 value = static_cast<int32>(value_data);
2868 value = static_cast<int16>(value_data);
2873 value = static_cast<int8>(value_data);
2877 case VOGL_GLINT64EXT:
2879 value = static_cast<int64_t>(value_data);
2889 if (value == value_to_find)
2895 //----------------------------------------------------------------------------------------------------------------------
2897 //----------------------------------------------------------------------------------------------------------------------
2898 static void print_match(const vogl_trace_packet &trace_packet, int param_index, int array_element_index, uint64_t total_swaps)
2901 vogl_trace_packet::json_serialize_params params;
2902 trace_packet.json_serialize(*doc.get_root(), params);
2904 dynamic_string packet_as_json;
2905 doc.serialize(packet_as_json);
2907 if (param_index == -2)
2908 vogl_printf("----- Function match, frame %" PRIu64 ":\n", total_swaps);
2909 else if (param_index < 0)
2910 vogl_printf("----- Return value match, frame %" PRIu64 ":\n", total_swaps);
2911 else if (array_element_index >= 0)
2912 vogl_printf("----- Parameter %i element %i match, frame %" PRIu64 ":\n", param_index, array_element_index, total_swaps);
2914 vogl_printf("----- Parameter %i match, frame %" PRIu64 ":\n", param_index, total_swaps);
2916 vogl_printf("%s\n", packet_as_json.get_ptr());
2919 //----------------------------------------------------------------------------------------------------------------------
2921 //----------------------------------------------------------------------------------------------------------------------
2922 static bool tool_find_mode()
2926 dynamic_string input_base_filename(g_command_line_params.get_value_as_string_or_empty("", 1));
2927 if (input_base_filename.is_empty())
2929 vogl_error_printf("Must specify filename of input JSON/blob trace files!\n");
2933 dynamic_string actual_input_filename;
2934 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()));
2935 if (!pTrace_reader.get())
2938 bigint128 value_to_find(static_cast<uint64_t>(gl_enums::cUnknownEnum));
2939 bool has_find_param = g_command_line_params.has_key("find_param");
2942 dynamic_string find_value_str(g_command_line_params.get_value_as_string("find_param"));
2943 if ((find_value_str.has_content()) && (vogl_isalpha(find_value_str[0])))
2945 value_to_find = g_gl_enums.find_enum(find_value_str);
2948 if (value_to_find == static_cast<uint64_t>(gl_enums::cUnknownEnum))
2950 bool find_param_u64_valid = false;
2951 value_to_find = g_command_line_params.get_value_as_uint64("find_param", 0, 0, 0, cUINT64_MAX, 0, &find_param_u64_valid);
2953 if (!find_param_u64_valid)
2955 bool find_param_i64_valid = false;
2956 value_to_find = g_command_line_params.get_value_as_int64("find_param", 0, 0, 0, cINT64_MAX, 0, &find_param_i64_valid);
2958 if (!find_param_i64_valid)
2960 vogl_error_printf("Failed parsing \"-find_param X\" command line option!\n");
2967 dynamic_string find_namespace_str(g_command_line_params.get_value_as_string_or_empty("find_namespace"));
2968 vogl_namespace_t find_namespace = VOGL_NAMESPACE_UNKNOWN;
2969 if (find_namespace_str.has_content())
2971 find_namespace = vogl_find_namespace_from_gl_type(find_namespace_str.get_ptr());
2972 if ((find_namespace == VOGL_NAMESPACE_INVALID) && (find_namespace_str != "invalid"))
2974 vogl_error_printf("Invalid namespace: \"%s\"\n", find_namespace_str.get_ptr());
2979 dynamic_string find_param_name(g_command_line_params.get_value_as_string_or_empty("find_param_name"));
2981 dynamic_string find_func_pattern(g_command_line_params.get_value_as_string_or_empty("find_func"));
2983 if (find_func_pattern.has_content())
2985 if (!func_regex.init(find_func_pattern.get_ptr(), REGEX_IGNORE_CASE))
2987 vogl_error_printf("Invalid func regex: \"%s\"\n", find_func_pattern.get_ptr());
2992 int64_t find_frame_low = g_command_line_params.get_value_as_int64("find_frame_low", 0, -1);
2993 int64_t find_frame_high = g_command_line_params.get_value_as_int64("find_frame_high", 0, -1);
2994 int64_t find_call_low = g_command_line_params.get_value_as_int64("find_call_low", 0, -1);
2995 int64_t find_call_high = g_command_line_params.get_value_as_int64("find_call_high", 0, -1);
2997 vogl_printf("Scanning trace file %s\n", actual_input_filename.get_ptr());
2999 vogl_ctypes trace_gl_ctypes(pTrace_reader->get_sof_packet().m_pointer_sizes);
3000 vogl_trace_packet trace_packet(&trace_gl_ctypes);
3002 uint64_t total_matches = 0;
3003 uint64_t total_swaps = 0;
3007 vogl_trace_file_reader::trace_file_reader_status_t read_status = pTrace_reader->read_next_packet();
3009 if ((read_status != vogl_trace_file_reader::cOK) && (read_status != vogl_trace_file_reader::cEOF))
3011 vogl_error_printf("Failed reading from trace file!\n");
3015 if (read_status == vogl_trace_file_reader::cEOF)
3018 const vogl::vector<uint8> &packet_buf = pTrace_reader->get_packet_buf();
3019 VOGL_NOTE_UNUSED(packet_buf);
3021 const vogl_trace_stream_packet_base &base_packet = pTrace_reader->get_base_packet();
3022 VOGL_NOTE_UNUSED(base_packet);
3024 const vogl_trace_gl_entrypoint_packet *pGL_packet = NULL;
3025 VOGL_NOTE_UNUSED(pGL_packet);
3027 if (pTrace_reader->get_packet_type() == cTSPTEOF)
3029 else if (pTrace_reader->get_packet_type() != cTSPTGLEntrypoint)
3032 if (!trace_packet.deserialize(pTrace_reader->get_packet_buf().get_ptr(), pTrace_reader->get_packet_buf().size(), false))
3034 console::error("%s: Failed parsing GL entrypoint packet\n", VOGL_FUNCTION_NAME);
3038 pGL_packet = &pTrace_reader->get_packet<vogl_trace_gl_entrypoint_packet>();
3040 if (find_frame_low >= 0)
3042 if (total_swaps < static_cast<uint64_t>(find_frame_low))
3045 if (find_frame_high >= 0)
3047 if (total_swaps > static_cast<uint64_t>(find_frame_high))
3051 if (find_call_low >= 0)
3053 if (trace_packet.get_call_counter() < static_cast<uint64_t>(find_call_low))
3056 if (find_call_high >= 0)
3058 if (trace_packet.get_call_counter() > static_cast<uint64_t>(find_call_high))
3062 if (func_regex.is_initialized())
3064 if (!func_regex.full_match(trace_packet.get_entrypoint_desc().m_pName))
3068 if (!has_find_param)
3070 print_match(trace_packet, -2, -1, total_swaps);
3075 if (trace_packet.has_return_value())
3077 if ((find_param_name.is_empty()) || (find_param_name == "return"))
3079 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()))
3081 print_match(trace_packet, -1, -1, total_swaps);
3087 for (uint i = 0; i < trace_packet.total_params(); i++)
3089 if ((find_param_name.has_content()) && (find_param_name != trace_packet.get_param_desc(i).m_pName))
3092 const vogl_ctype_desc_t ¶m_ctype_desc = trace_packet.get_param_ctype_desc(i);
3094 if (param_ctype_desc.m_is_pointer)
3096 if ((!param_ctype_desc.m_is_opaque_pointer) && (param_ctype_desc.m_pointee_ctype != VOGL_VOID) && (trace_packet.has_param_client_memory(i)))
3098 const vogl_client_memory_array array(trace_packet.get_param_client_memory_array(i));
3100 for (uint j = 0; j < array.size(); j++)
3102 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)))
3104 print_match(trace_packet, i, j, total_swaps);
3110 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)))
3112 print_match(trace_packet, i, -1, total_swaps);
3119 if (vogl_is_swap_buffers_entrypoint(trace_packet.get_entrypoint_id()))
3124 vogl_printf("Total matches found: %" PRIu64 "\n", total_matches);
3129 //----------------------------------------------------------------------------------------------------------------------
3130 // tool_compare_hash_files
3131 //----------------------------------------------------------------------------------------------------------------------
3132 static bool tool_compare_hash_files()
3134 dynamic_string src1_filename(g_command_line_params.get_value_as_string_or_empty("", 1));
3135 dynamic_string src2_filename(g_command_line_params.get_value_as_string_or_empty("", 2));
3136 if ((src1_filename.is_empty()) || (src2_filename.is_empty()))
3138 vogl_error_printf("Must specify two source filenames!\n");
3142 dynamic_string_array src1_lines;
3143 if (!file_utils::read_text_file(src1_filename.get_ptr(), src1_lines, file_utils::cRTFTrim | file_utils::cRTFIgnoreEmptyLines | file_utils::cRTFIgnoreCommentedLines | file_utils::cRTFPrintErrorMessages))
3145 console::error("%s: Failed reading source file %s!\n", VOGL_FUNCTION_NAME, src1_filename.get_ptr());
3149 vogl_printf("Read 1st source file \"%s\", %u lines\n", src1_filename.get_ptr(), src1_lines.size());
3151 dynamic_string_array src2_lines;
3152 if (!file_utils::read_text_file(src2_filename.get_ptr(), src2_lines, file_utils::cRTFTrim | file_utils::cRTFIgnoreEmptyLines | file_utils::cRTFIgnoreCommentedLines | file_utils::cRTFPrintErrorMessages))
3154 console::error("%s: Failed reading source file %s!\n", VOGL_FUNCTION_NAME, src2_filename.get_ptr());
3158 vogl_printf("Read 2nd source file \"%s\", %u lines\n", src2_filename.get_ptr(), src2_lines.size());
3160 const uint64_t sum_comp_thresh = g_command_line_params.get_value_as_uint64("sum_compare_threshold");
3162 const uint compare_first_frame = g_command_line_params.get_value_as_uint("compare_first_frame");
3163 if (compare_first_frame > src2_lines.size())
3165 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());
3169 const uint lines_to_comp = math::minimum(src1_lines.size(), src2_lines.size() - compare_first_frame);
3171 if (src1_lines.size() != src2_lines.size())
3173 // FIXME: When we replay q2, we get 2 more frames vs. tracing. Not sure why, this needs to be investigated.
3174 if ( (!g_command_line_params.get_value_as_bool("ignore_line_count_differences")) && (labs(src1_lines.size() - src2_lines.size()) > 3) )
3176 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());
3181 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());
3185 const uint compare_ignore_frames = g_command_line_params.get_value_as_uint("compare_ignore_frames");
3186 if (compare_ignore_frames > lines_to_comp)
3188 vogl_error_printf("%s: -compare_ignore_frames is too large!\n", VOGL_FUNCTION_NAME);
3192 const bool sum_hashing = g_command_line_params.get_value_as_bool("sum_hashing");
3194 if (g_command_line_params.has_key("compare_expected_frames"))
3196 const uint compare_expected_frames = g_command_line_params.get_value_as_uint("compare_expected_frames");
3197 if ((src1_lines.size() != compare_expected_frames) || (src2_lines.size() != compare_expected_frames))
3199 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());
3204 uint total_mismatches = 0;
3205 uint64_t max_sum_delta = 0;
3207 for (uint i = compare_ignore_frames; i < lines_to_comp; i++)
3209 const char *pStr1 = src1_lines[i].get_ptr();
3210 const char *pStr2 = src2_lines[i + compare_first_frame].get_ptr();
3212 uint64_t val1 = 0, val2 = 0;
3213 if (!string_ptr_to_uint64(pStr1, val1))
3215 vogl_error_printf("%s: Failed parsing line at index %u of first source file!\n", VOGL_FUNCTION_NAME, i);
3219 if (!string_ptr_to_uint64(pStr2, val2))
3221 vogl_error_printf("%s: Failed parsing line at index %u of second source file!\n", VOGL_FUNCTION_NAME, i);
3225 bool mismatch = false;
3231 delta = val1 - val2;
3233 delta = val2 - val1;
3234 max_sum_delta = math::maximum(max_sum_delta, delta);
3236 if (delta > sum_comp_thresh)
3241 mismatch = val1 != val2;
3247 vogl_error_printf("Mismatch at frame %u: %" PRIu64 ", %" PRIu64 "\n", i, val1, val2);
3249 vogl_error_printf("Mismatch at frame %u: 0x%" PRIX64 ", 0x%" PRIX64 "\n", i, val1, val2);
3255 vogl_printf("Max sum delta: %" PRIu64 "\n", max_sum_delta);
3257 if (!total_mismatches)
3258 vogl_printf("No mismatches\n");
3261 vogl_error_printf("%u total mismatches!\n", total_mismatches);
3268 //----------------------------------------------------------------------------------------------------------------------
3269 // tool_unpack_json_mode
3270 //----------------------------------------------------------------------------------------------------------------------
3271 static bool tool_unpack_json_mode()
3275 dynamic_string input_filename(g_command_line_params.get_value_as_string_or_empty("", 1));
3276 if (input_filename.is_empty())
3278 vogl_error_printf("Must specify filename of input UBJ file!\n");
3282 dynamic_string output_filename(g_command_line_params.get_value_as_string_or_empty("", 2));
3283 if (output_filename.is_empty())
3285 vogl_error_printf("Must specify filename of output text file!\n");
3290 vogl_message_printf("Reading UBJ file \"%s\"\n", input_filename.get_ptr());
3292 if (!doc.binary_deserialize_file(input_filename.get_ptr()))
3294 vogl_error_printf("Unable to deserialize input file \"%s\"!\n", input_filename.get_ptr());
3295 if (doc.get_error_msg().has_content())
3296 vogl_error_printf("%s\n", doc.get_error_msg().get_ptr());
3300 if (!doc.serialize_to_file(output_filename.get_ptr()))
3302 vogl_error_printf("Failed serializing to output file \"%s\"!\n", output_filename.get_ptr());
3306 vogl_message_printf("Wrote textual JSON file to \"%s\"\n", output_filename.get_ptr());
3311 //----------------------------------------------------------------------------------------------------------------------
3312 // tool_pack_json_mode
3313 //----------------------------------------------------------------------------------------------------------------------
3314 static bool tool_pack_json_mode()
3318 dynamic_string input_filename(g_command_line_params.get_value_as_string_or_empty("", 1));
3319 if (input_filename.is_empty())
3321 vogl_error_printf("Must specify filename of input text file!\n");
3325 dynamic_string output_filename(g_command_line_params.get_value_as_string_or_empty("", 2));
3326 if (output_filename.is_empty())
3328 vogl_error_printf("Must specify filename of output UBJ file!\n");
3333 vogl_message_printf("Reading JSON text file \"%s\"\n", input_filename.get_ptr());
3335 if (!doc.deserialize_file(input_filename.get_ptr()))
3337 vogl_error_printf("Unable to deserialize input file \"%s\"!\n", input_filename.get_ptr());
3338 if (doc.get_error_msg().has_content())
3339 vogl_error_printf("%s (Line: %u)\n", doc.get_error_msg().get_ptr(), doc.get_error_line());
3343 if (!doc.binary_serialize_to_file(output_filename.get_ptr()))
3345 vogl_error_printf("Failed serializing to output file \"%s\"!\n", output_filename.get_ptr());
3349 vogl_message_printf("Wrote binary UBJ file to \"%s\"\n", output_filename.get_ptr());
3354 //----------------------------------------------------------------------------------------------------------------------
3356 //----------------------------------------------------------------------------------------------------------------------
3357 static int xerror_handler(Display *dsp, XErrorEvent *error)
3359 char error_string[256];
3360 XGetErrorText(dsp, error->error_code, error_string, sizeof(error_string));
3362 fprintf(stderr, "voglreplay: Fatal X Windows Error: %s\n", error_string);
3366 //----------------------------------------------------------------------------------------------------------------------
3368 //----------------------------------------------------------------------------------------------------------------------
3369 int main(int argc, char *argv[])
3371 #if VOGL_FUNCTION_TRACING
3374 setvbuf(stdout, NULL, _IONBF, 0);
3375 setvbuf(stderr, NULL, _IONBF, 0);
3380 XSetErrorHandler(xerror_handler);
3382 if (!vogl_replay_init(argc, argv))
3384 vogl_replay_deinit();
3385 return EXIT_FAILURE;
3389 vogl_init_listener();
3390 #endif // VOGL_REMOTING
3394 return EXIT_SUCCESS;
3397 if (g_command_line_params.get_count("") < 2)
3399 vogl_error_printf("Must specify at least one trace (or input) files!\n");
3403 vogl_replay_deinit();
3404 return EXIT_FAILURE;
3407 if (g_command_line_params.get_value_as_bool("pause"))
3409 vogl_message_printf("Press key to continue\n");
3414 bool success = false;
3416 if (g_command_line_params.get_value_as_bool("dump"))
3418 tmZone(TELEMETRY_LEVEL0, TMZF_NONE, "dump");
3419 vogl_message_printf("Dump from binary to JSON mode\n");
3421 success = tool_dump_mode();
3423 else if (g_command_line_params.get_value_as_bool("parse"))
3425 tmZone(TELEMETRY_LEVEL0, TMZF_NONE, "parse");
3426 vogl_message_printf("Parse from JSON to binary mode\n");
3428 success = tool_parse_mode();
3430 else if (g_command_line_params.get_value_as_bool("info"))
3432 tmZone(TELEMETRY_LEVEL0, TMZF_NONE, "info");
3433 vogl_message_printf("Dumping trace information\n");
3435 success = tool_info_mode();
3437 else if (g_command_line_params.get_value_as_bool("unpack_json"))
3439 tmZone(TELEMETRY_LEVEL0, TMZF_NONE, "unpack_json");
3440 vogl_message_printf("Unpacking UBJ to text\n");
3442 success = tool_unpack_json_mode();
3444 else if (g_command_line_params.get_value_as_bool("pack_json"))
3446 tmZone(TELEMETRY_LEVEL0, TMZF_NONE, "pack_json");
3447 vogl_message_printf("Packing textual JSON to UBJ\n");
3449 success = tool_pack_json_mode();
3451 else if (g_command_line_params.get_value_as_bool("find"))
3453 vogl_message_printf("Find mode\n");
3455 success = tool_find_mode();
3457 else if (g_command_line_params.get_value_as_bool("compare_hash_files"))
3459 vogl_message_printf("Comparing hash/sum files\n");
3461 success = tool_compare_hash_files();
3465 tmZone(TELEMETRY_LEVEL0, TMZF_NONE, "replay_mode");
3466 vogl_message_printf("Replay mode\n");
3468 success = tool_replay_mode();
3471 console::printf("%u warning(s), %u error(s)\n", console::get_total_messages(cWarningConsoleMessage), console::get_total_messages(cErrorConsoleMessage));
3473 vogl_replay_deinit();
3475 return success ? EXIT_SUCCESS : EXIT_FAILURE;