//---------------------------------------------------------------------------------------------------------------------- // tool_print_help //---------------------------------------------------------------------------------------------------------------------- static void tool_print_help() { VOGL_FUNC_TRACER vogl_printf("Usage: voglsyms [ -option ... ] input_file optional_output_file [ -option ... ]\n"); vogl_printf("Command line options may begin with single minus \"-\" or double minus \"--\"\n"); vogl_printf("\nCommand line options:\n"); dump_command_line_info(VOGL_ARRAY_SIZE(g_command_line_param_descs), g_command_line_param_descs, "--"); }
//---------------------------------------------------------------------------------------------------------------------- // tool_print_title //---------------------------------------------------------------------------------------------------------------------- static void tool_print_title() { VOGL_FUNC_TRACER printf("vogl "); if (sizeof(void *) > 4) vogl_printf("64-bit "); else vogl_printf("32-bit "); #ifdef VOGL_BUILD_DEBUG vogl_printf("Debug "); #else vogl_printf("Release "); #endif vogl_printf("Built %s %s\n", __DATE__, __TIME__); }
//---------------------------------------------------------------------------------------------------------------------- // dump_compiler_info //---------------------------------------------------------------------------------------------------------------------- static bool dump_compiler_info(vogl_trace_file_reader *pTrace_reader, dynamic_string &tracefile_arch) { if (pTrace_reader->get_archive_blob_manager().does_exist(VOGL_TRACE_ARCHIVE_COMPILER_INFO_FILENAME)) { vogl_header1_printf("%s\n", std::string(78, '*').c_str()); vogl_header1_printf("%s\n", VOGL_TRACE_ARCHIVE_COMPILER_INFO_FILENAME); vogl_header1_printf("%s\n", std::string(78, '*').c_str()); uint8_vec machine_info_data; if (pTrace_reader->get_archive_blob_manager().get(VOGL_TRACE_ARCHIVE_COMPILER_INFO_FILENAME, machine_info_data)) { json_document doc; json_node *pRoot = doc.get_root(); if (doc.deserialize((const char *)machine_info_data.get_ptr(), machine_info_data.size()) && pRoot->size()) { for (uint i = 0; i < pRoot->size(); i++) { const dynamic_string §ionKeyStr = pRoot->get_key(i); vogl_message_printf("%s\n", sectionKeyStr.c_str()); const vogl::json_value &value = pRoot->get_value(i); const json_node *pNode = value.get_node_ptr(); // glinfo, uname, etc. for (uint j = 0; j < pNode->size(); j++) { dynamic_string key = pNode->get_key(j); const vogl::json_value &val = pNode->get_value(j); dynamic_string str = val.as_string(); vogl_printf(" %s: %s\n", key.c_str(), str.c_str()); if (!key.compare("arch")) tracefile_arch = str; } } vogl_printf("\n"); return true; } } } return false; }
//---------------------------------------------------------------------------------------------------------------------- // vogl_direct_gl_func_epilog - This function is called immediately after EVERY single GL/GLX function call we make. //---------------------------------------------------------------------------------------------------------------------- static void vogl_direct_gl_func_epilog(gl_entrypoint_id_t entrypoint_id, void *pUser_data, void **ppStack_data) { VOGL_NOTE_UNUSED(entrypoint_id); VOGL_NOTE_UNUSED(pUser_data); VOGL_NOTE_UNUSED(ppStack_data); vogl_printf("* GLEPILOG %s\n", g_vogl_entrypoint_descs[entrypoint_id].m_pName); }
//---------------------------------------------------------------------------------------------------------------------- // print_match //---------------------------------------------------------------------------------------------------------------------- static void print_match(const vogl_trace_packet &trace_packet, int param_index, int array_element_index, uint64_t total_swaps) { json_document doc; vogl_trace_packet::json_serialize_params params; trace_packet.json_serialize(*doc.get_root(), params); dynamic_string packet_as_json; doc.serialize(packet_as_json); if (param_index == -2) vogl_printf("----- Function match, frame %" PRIu64 ":\n", total_swaps); else if (param_index < 0) vogl_printf("----- Return value match, frame %" PRIu64 ":\n", total_swaps); else if (array_element_index >= 0) vogl_printf("----- Parameter %i element %i match, frame %" PRIu64 ":\n", param_index, array_element_index, total_swaps); else vogl_printf("----- Parameter %i match, frame %" PRIu64 ":\n", param_index, total_swaps); vogl_printf("%s\n", packet_as_json.get_ptr()); }
//---------------------------------------------------------------------------------------------------------------------- // main //---------------------------------------------------------------------------------------------------------------------- int main(int argc, char *argv[]) { #if VOGL_FUNCTION_TRACING fflush(stdout); fflush(stderr); setvbuf(stdout, NULL, _IONBF, 0); setvbuf(stderr, NULL, _IONBF, 0); #endif VOGL_FUNC_TRACER // Initialize vogl_core. vogl_core_init(); XSetErrorHandler(xerror_handler); if (!voglbench_init(argc, argv)) { voglbench_deinit(); return EXIT_FAILURE; } if (g_command_line_params().get_count("") < 2) { vogl_error_printf("No trace file specified!\n"); tool_print_help(); voglbench_deinit(); return EXIT_FAILURE; } if (g_command_line_params().get_value_as_bool("pause")) { vogl_message_printf("Press key to continue\n"); vogl_sleep(1000); getchar(); } bool success = tool_replay_mode(); vogl_printf("%u warning(s), %u error(s)\n", console::get_total_messages(cWarningConsoleMessage), console::get_total_messages(cErrorConsoleMessage)); voglbench_deinit(); return success ? EXIT_SUCCESS : EXIT_FAILURE; }
//---------------------------------------------------------------------------------------------------------------------- // voglsym_main_loop //---------------------------------------------------------------------------------------------------------------------- static bool voglsym_main_loop(char *argv[]) { VOGL_FUNC_TRACER dynamic_string tracefile_arch; dynamic_string actual_trace_filename; vector<addr_data_t> addr_data_arr; vector<btrace_module_info> module_infos; dynamic_string trace_filename(g_command_line_params().get_value_as_string_or_empty("", 1)); if (trace_filename.is_empty()) { vogl_error_printf("%s: No trace file specified!\n", VOGL_FUNCTION_NAME); return false; } 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())); if (!pTrace_reader.get()) { 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()); return false; } bool resolve_symbols = g_command_line_params().get_value_as_bool("resolve_symbols"); if (resolve_symbols) vogl_printf("Resolving symbols in trace file %s\n\n", actual_trace_filename.get_ptr()); else vogl_printf("Reading trace file %s\n\n", actual_trace_filename.get_ptr()); // compiler_info.json dump_compiler_info(pTrace_reader.get(), tracefile_arch); if (resolve_symbols) { if (tracefile_arch.size()) { bool is_64bit = (sizeof(void *) == 8); bool trace_file_arch_is_64bits = !tracefile_arch.compare("64bit"); if (trace_file_arch_is_64bits != is_64bit) { const char *arch_str = is_64bit ? "64-bit" : "32-bit"; vogl_error_printf("ERROR: %s is %s, tracefile is %s.\n", argv[0], arch_str, tracefile_arch.c_str()); vogl_error_printf("ERROR: Same architecture required to resolve symbols.\n"); return -1; } } if (pTrace_reader->get_archive_blob_manager().does_exist(VOGL_TRACE_ARCHIVE_BACKTRACE_MAP_SYMS_FILENAME)) { vogl_error_printf("ERROR: Symbols have already resolved for this tracefile.\n"); return -1; } } // machine_info.json dump_machine_info(pTrace_reader.get(), module_infos); // backtrace_map_addrs.json get_backtrace_map_addrs(pTrace_reader.get(), addr_data_arr); // backtrace_map_syms.json dump_backtrace_map_syms(pTrace_reader.get()); // Spew our module information if (module_infos.size()) { vogl_header1_printf("%s\n", std::string(78, '*').c_str()); vogl_header1_printf("%s\n", "Modules"); vogl_header1_printf("%s\n", std::string(78, '*').c_str()); for (uint i = 0; i < module_infos.size(); i++) { char uuid_str[41]; const btrace_module_info &module_info = module_infos[i]; btrace_uuid_to_str(uuid_str, module_info.uuid, module_info.uuid_len); vogl_printf("0x%" PRIxPTR " (%u) %s %s %s", module_info.base_address, module_info.address_size, uuid_str, module_info.filename, module_info.is_exe ? "(exe)" : ""); if (resolve_symbols) { const char *debug_filename = NULL; if (btrace_dlopen_add_module(module_info)) { debug_filename = btrace_get_debug_filename(module_info.filename); } if (debug_filename) { vogl_printf(" [%s]", debug_filename); } } vogl_printf("\n"); } vogl_printf("\n"); } // Spew our backtrace addresses if (addr_data_arr.size()) { vogl_header1_printf("%s\n", std::string(78, '*').c_str()); vogl_header1_printf("%s\n", VOGL_TRACE_ARCHIVE_BACKTRACE_MAP_ADDRS_FILENAME); vogl_header1_printf("%s\n", std::string(78, '*').c_str()); vogl_printf("index (count): addr0, addr1, ...\n"); for (uint i = 0; i < addr_data_arr.size(); i++) { const addr_data_t &addr_data = addr_data_arr[i]; vogl_printf("0x%x (%u): ", addr_data.index, addr_data.count); for (uint j = 0; j < addr_data.addrs.size(); j++) { vogl_printf("%" PRIxPTR " ", addr_data.addrs[j]); } vogl_printf("\n"); } vogl_printf("\n"); } // Resolve symbols if we're supposed to. //$ TODO: The resolve symbols should be added to the trace file? if (resolve_symbols) { json_document doc; json_node *pRoot = doc.get_root(); pRoot->init_array(); vogl_header1_printf("%s\n", std::string(78, '*').c_str()); vogl_header1_printf("%s\n", "Resolving symbols..."); vogl_header1_printf("%s\n", std::string(78, '*').c_str()); for (uint i = 0; i < addr_data_arr.size(); i++) { const addr_data_t &addr_data = addr_data_arr[i]; json_node &syms_arr = pRoot->add_array(); for (uint j = 0; j < addr_data.addrs.size(); j++) { btrace_info trace_info; uintptr_t addr = addr_data.addrs[j]; bool success = btrace_resolve_addr(&trace_info, addr, BTRACE_RESOLVE_ADDR_GET_FILENAME | BTRACE_RESOLVE_ADDR_DEMANGLE_FUNC); dynamic_string sym; if (!success) { sym = "?"; } else if (trace_info.function[0] && trace_info.filename[0]) { // Got function and/or filename. sym.format("%s (%s+0x%" PRIx64 ") at %s:%i", trace_info.function, trace_info.module[0] ? trace_info.module : "?", cast_val_to_uint64(trace_info.offset), trace_info.filename, trace_info.linenumber); } else if (trace_info.function[0]) { // Got function, no filename. sym.format("%s (%s+0x%" PRIx64 ")", trace_info.function, trace_info.module[0] ? trace_info.module : "?", cast_val_to_uint64(trace_info.offset)); } else { // Only got modulename (no debugging information found). sym.format("(%s+0x%" PRIx64 ")", trace_info.module[0] ? trace_info.module : "?", cast_val_to_uint64(trace_info.offset)); } syms_arr.add_value(sym); } } doc.print(true, 0, 0); } return true; }
//---------------------------------------------------------------------------------------------------------------------- // dump_machine_info //---------------------------------------------------------------------------------------------------------------------- static bool dump_machine_info(vogl_trace_file_reader *pTrace_reader, vector<btrace_module_info> &module_infos) { if (pTrace_reader->get_archive_blob_manager().does_exist(VOGL_TRACE_ARCHIVE_MACHINE_INFO_FILENAME)) { vogl_header1_printf("%s\n", std::string(78, '*').c_str()); vogl_header1_printf("%s\n", VOGL_TRACE_ARCHIVE_MACHINE_INFO_FILENAME); vogl_header1_printf("%s\n", std::string(78, '*').c_str()); uint8_vec machine_info_data; if (pTrace_reader->get_archive_blob_manager().get(VOGL_TRACE_ARCHIVE_MACHINE_INFO_FILENAME, machine_info_data)) { json_document doc; json_node *pRoot = doc.get_root(); if (doc.deserialize((const char *)machine_info_data.get_ptr(), machine_info_data.size()) && pRoot->size()) { for (uint i = 0; i < pRoot->size(); i++) { const dynamic_string §ionKeyStr = pRoot->get_key(i); const vogl::json_value &value = pRoot->get_value(i); const json_node *pNode = value.get_node_ptr(); bool is_module_list = (sectionKeyStr == "module_list") && value.is_node(); if (!is_module_list) { vogl_message_printf("%s\n", sectionKeyStr.c_str()); } if (is_module_list) { for (uint j = 0; j < pNode->size(); j++) { dynamic_string key = pNode->get_key(j); const vogl::json_value &val = pNode->get_value(j); // key is filename, addr_base, addr_size, uuid, is_exe if (val.is_array()) { uint index = 0; const json_node *pNode2 = val.get_node_ptr(); uint size = pNode2->size(); btrace_module_info module_info; memset(&module_info, 0, sizeof(module_info)); module_info.filename = vogl::vogl_strdup(key.c_str()); module_info.base_address = (index < size) ? (uintptr_t)pNode2->get_value(index++).as_uint64() : 0; module_info.address_size = (index < size) ? pNode2->get_value(index++).as_uint32() : 0; const char *uuid_str = (index < size) ? pNode2->get_value(index++).as_string_ptr() : "--"; module_info.is_exe = (index < size) ? pNode2->get_value(index++).as_string() == "(exe)" : false; module_info.uuid_len = btrace_uuid_str_to_uuid(module_info.uuid, uuid_str); module_infos.push_back(module_info); } } } else if (value.is_array()) { // environ_list, cmdline, etc. for (uint element = 0; element < pNode->size(); element++) { dynamic_string str = pNode->get_value(element).as_string(); vogl_printf(" %s\n", str.c_str()); } vogl_printf("\n"); } else if (value.is_node()) { // glinfo, uname, etc. for (uint j = 0; j < pNode->size(); j++) { dynamic_string key = pNode->get_key(j); const vogl::json_value &val = pNode->get_value(j); dynamic_string str = val.as_string(); if (val.is_array()) { const json_node *pNode2 = val.get_node_ptr(); vogl_printf("%s\n", key.c_str()); for (uint element = 0; element < pNode2->size(); element++) { dynamic_string str2 = pNode2->get_value(element).as_string(); vogl_printf(" %s\n", str2.c_str()); } } else { vogl_printf(" %s: %s\n", key.c_str(), str.c_str()); } } vogl_printf("\n"); } else { dynamic_string str = value.as_string(); vogl_printf("%s\n", str.c_str()); } } } return true; } } return false; }
// TODO: return bool void vogleditor_QApiCallTreeModel::setupModelData(vogl_trace_file_reader* pTrace_reader, vogleditor_apiCallTreeItem *parent) { const vogl_trace_stream_start_of_file_packet &sof_packet = pTrace_reader->get_sof_packet(); VOGL_NOTE_UNUSED(sof_packet); uint64_t total_swaps = 0; // TODO probably need to handle eof_packet //bool found_eof_packet = false; // make a PendingFrame node to hold the api calls // this will remain in the pending state until the first // api call is seen, then it will be made the CurFrame and // appended to the parent vogleditor_frameItem* pCurFrame = NULL; vogleditor_apiCallTreeItem* pCurParent = parent; // Make a PendingSnapshot that may or may not be populated when reading the trace. // This snapshot will be assigned to the next API call that occurs. vogleditor_gl_state_snapshot* pPendingSnapshot = NULL; m_trace_ctypes.init(pTrace_reader->get_sof_packet().m_pointer_sizes); for ( ; ; ) { vogl_trace_file_reader::trace_file_reader_status_t read_status = pTrace_reader->read_next_packet(); if ((read_status != vogl_trace_file_reader::c*K) && (read_status != vogl_trace_file_reader::cEOF)) { vogl_error_printf("Failed reading from trace file!\n"); break; } if (read_status == vogl_trace_file_reader::cEOF) { vogl_printf("At trace file EOF on swap %" PRIu64 "\n", total_swaps); break; } const vogl::vector<uint8> &packet_buf = pTrace_reader->get_packet_buf(); VOGL_NOTE_UNUSED(packet_buf); const vogl_trace_stream_packet_base &base_packet = pTrace_reader->get_base_packet(); VOGL_NOTE_UNUSED(base_packet); const vogl_trace_gl_entrypoint_packet *pGL_packet = NULL; if (pTrace_reader->get_packet_type() == cTSPTGLEntrypoint) { vogl_trace_packet* pTrace_packet = vogl_new(vogl_trace_packet, &m_trace_ctypes); if (!pTrace_packet->deserialize(pTrace_reader->get_packet_buf().get_ptr(), pTrace_reader->get_packet_buf().size(), false)) { console::error("%s: Failed parsing GL entrypoint packet\n", VOGL_FUNCTION_NAME); break; } pGL_packet = &pTrace_reader->get_packet<vogl_trace_gl_entrypoint_packet>(); gl_entrypoint_id_t entrypoint_id = static_cast<gl_entrypoint_id_t>(pGL_packet->m_entrypoint_id); if (entrypoint_id == VOGL_ENTRYPOINT_glInternalTraceCommandRAD) { // Check if this is a state snapshot. // This is entirely optional since the client is designed to dynamically get new snapshots // if they don't exist. GLuint cmd = pTrace_packet->get_param_value<GLuint>(0); GLuint size = pTrace_packet->get_param_value<GLuint>(1); VOGL_NOTE_UNUSED(size); if (cmd == cITCRKeyValueMap) { key_value_map &kvm = pTrace_packet->get_key_value_map(); dynamic_string cmd_type(kvm.get_string("command_type")); if (cmd_type == "state_snapshot") { dynamic_string id(kvm.get_string("binary_id")); if (id.is_empty()) { vogl_warning_printf("%s: Missing binary_id field in glInternalTraceCommandRAD key_valye_map command type: \"%s\"\n", VOGL_FUNCTION_NAME, cmd_type.get_ptr()); continue; } uint8_vec snapshot_data; { timed_scope ts("get_multi_blob_manager().get"); if (!pTrace_reader->get_multi_blob_manager().get(id, snapshot_data) || (snapshot_data.is_empty())) { vogl_warning_printf("%s: Failed reading snapshot blob data \"%s\"!\n", VOGL_FUNCTION_NAME, id.get_ptr()); continue; } } json_document doc; { timed_scope ts("doc.binary_deserialize"); if (!doc.binary_deserialize(snapshot_data) || (!doc.get_root())) { vogl_warning_printf("%s: Failed deserializing JSON snapshot blob data \"%s\"!\n", VOGL_FUNCTION_NAME, id.get_ptr()); continue; } } vogl_gl_state_snapshot* pGLSnapshot = vogl_new(vogl_gl_state_snapshot); pPendingSnapshot = vogl_new(vogleditor_gl_state_snapshot, pGLSnapshot); timed_scope ts("pPendingSnapshot->deserialize"); if (!pPendingSnapshot->get_snapshot()->deserialize(*doc.get_root(), pTrace_reader->get_multi_blob_manager(), &m_trace_ctypes)) { vogl_delete(pPendingSnapshot); pPendingSnapshot = NULL; vogl_warning_printf("%s: Failed deserializing snapshot blob data \"%s\"!\n", VOGL_FUNCTION_NAME, id.get_ptr()); } } } continue; } const gl_entrypoint_desc_t &entrypoint_desc = g_vogl_entrypoint_descs[entrypoint_id]; QString funcCall = entrypoint_desc.m_pName; // format parameters funcCall.append("( "); dynamic_string paramStr; for (uint param_index = 0; param_index < pTrace_packet->total_params(); param_index++) { if (param_index != 0) funcCall.append(", "); paramStr.clear(); pTrace_packet->pretty_print_param(paramStr, param_index, false); funcCall.append(paramStr.c_str()); } funcCall.append(" )"); if (pTrace_packet->has_return_value()) { funcCall.append(" = "); paramStr.clear(); pTrace_packet->pretty_print_return_value(paramStr, false); funcCall.append(paramStr.c_str()); } // if we don't have a current frame, make a new frame node // and append it to the parent if (pCurFrame == NULL) { pCurFrame = vogl_new(vogleditor_frameItem, total_swaps); vogleditor_apiCallTreeItem* pNewFrameNode = vogl_new(vogleditor_apiCallTreeItem, pCurFrame, pCurParent); pCurParent->appendChild(pNewFrameNode); m_itemList.append(pNewFrameNode); if (pPendingSnapshot != NULL) { pCurFrame->set_snapshot(pPendingSnapshot); pPendingSnapshot = NULL; } // update current parent pCurParent = pNewFrameNode; } // make item and node for the api call vogleditor_apiCallItem* pCallItem = vogl_new(vogleditor_apiCallItem, pCurFrame, pTrace_packet, *pGL_packet); pCurFrame->appendCall(pCallItem); if (pPendingSnapshot != NULL) { pCallItem->set_snapshot(pPendingSnapshot); pPendingSnapshot = NULL; } vogleditor_apiCallTreeItem* item = vogl_new(vogleditor_apiCallTreeItem, funcCall, pCallItem, pCurParent); pCurParent->appendChild(item); m_itemList.append(item); if (vogl_is_swap_buffers_entrypoint(entrypoint_id)) { total_swaps++; // reset the CurParent back to the original parent so that the next frame will be at the root level pCurParent = parent; // reset the CurFrame so that a new frame node will be created on the next api call pCurFrame = NULL; } else if (vogl_is_start_nested_entrypoint(entrypoint_id)) { // Nest logically paired blocks of gl calls including terminating // nest call pCurParent = item; } else if (vogl_is_end_nested_entrypoint(entrypoint_id)) { // move the parent back one level of the hierarchy, to its own parent // (but not past Frame parent [e.g., unpaired "end" operation]) if (pCurParent->parent() != parent) pCurParent = pCurParent->parent(); } } if (pTrace_reader->get_packet_type() == cTSPTEOF) { //found_eof_packet = true; vogl_printf("Found trace file EOF packet on swap %" PRIu64 "\n", total_swaps); break; } } }
//---------------------------------------------------------------------------------------------------------------------- // tool_replay_mode //---------------------------------------------------------------------------------------------------------------------- static bool tool_replay_mode() { VOGL_FUNC_TRACER dynamic_string trace_filename(g_command_line_params().get_value_as_string_or_empty("", 1)); if (trace_filename.is_empty()) { vogl_error_printf("No trace file specified!\n"); return false; } dynamic_string actual_trace_filename; 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() ) ); if (!pTrace_reader.get()) { vogl_error_printf("File not found, or unable to determine file type of trace file \"%s\"\n", trace_filename.get_ptr()); return false; } vogl_printf("Reading trace file %s\n", actual_trace_filename.get_ptr()); vogl_gl_replayer replayer; vogl_replay_window window; #if defined(PLATFORM_WINDOWS) // We need to get proc addresses for windows late. replayer.set_proc_address_helper(vogl_get_proc_address_helper, false); #endif uint replayer_flags = get_replayer_flags_from_command_line_params(); // TODO: This will create a window with default attributes, which seems fine for the majority of traces. // 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. // Also, this design only supports a single window, which is going to be a problem with multiple window traces. 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))) { vogl_error_printf("Failed initializing replay window\n"); return false; } if (!replayer.init(replayer_flags, &window, pTrace_reader->get_sof_packet(), pTrace_reader->get_multi_blob_manager())) { vogl_error_printf("Failed initializing GL replayer\n"); return false; } // Disable all glGetError() calls in vogl_utils.cpp. vogl_disable_gl_get_error(); // Bool win_mapped = false; vogl_gl_state_snapshot *pSnapshot = NULL; int64_t snapshot_loop_start_frame = -1; int64_t snapshot_loop_end_frame = -1; vogl::hash_map<SDL_Keycode> keys_pressed, keys_down; int loop_frame = g_command_line_params().get_value_as_int("loop_frame", 0, -1); int loop_len = math::maximum<int>(g_command_line_params().get_value_as_int("loop_len", 0, 1), 1); int loop_count = math::maximum<int>(g_command_line_params().get_value_as_int("loop_count", 0, cINT32_MAX), 1); bool endless_mode = g_command_line_params().get_value_as_bool("endless"); timer tm; tm.start(); for (;;) { tmZone(TELEMETRY_LEVEL0, TMZF_NONE, "Main Loop"); SDL_Event wnd_event; while (SDL_PollEvent(&wnd_event)) { switch (wnd_event.type) { case SDL_KEYDOWN: { keys_down.insert(wnd_event.key.keysym.sym); keys_pressed.insert(wnd_event.key.keysym.sym); break; } case SDL_KEYUP: { keys_down.erase(wnd_event.key.keysym.sym); break; } case SDL_WINDOWEVENT: { switch(wnd_event.window.event) { case SDL_WINDOWEVENT_FOCUS_GAINED: case SDL_WINDOWEVENT_FOCUS_LOST: keys_down.reset(); break; case SDL_WINDOWEVENT_CLOSE: vogl_message_printf("Window told to close, exiting.\n"); goto normal_exit; break; default: break; }; break; } default: // TODO: Handle these somehow? break; }; } if (replayer.get_at_frame_boundary()) { if ((!pSnapshot) && (loop_frame != -1) && (static_cast<int64_t>(replayer.get_frame_index()) == loop_frame)) { vogl_debug_printf("Capturing replayer state at start of frame %u\n", replayer.get_frame_index()); pSnapshot = replayer.snapshot_state(); if (pSnapshot) { vogl_printf("Snapshot succeeded\n"); snapshot_loop_start_frame = pTrace_reader->get_cur_frame(); snapshot_loop_end_frame = pTrace_reader->get_cur_frame() + loop_len; vogl_debug_printf("Loop start: %" PRIi64 " Loop end: %" PRIi64 "\n", snapshot_loop_start_frame, snapshot_loop_end_frame); } else { vogl_error_printf("Snapshot failed!\n"); loop_frame = -1; } } } vogl_gl_replayer::status_t status = replayer.process_pending_window_resize(); if (status == vogl_gl_replayer::cStatusOK) { for (;;) { status = replayer.process_pending_packets(); if (status == vogl_gl_replayer::cStatusOK) { status = replayer.process_next_packet(*pTrace_reader); } if ((status == vogl_gl_replayer::cStatusNextFrame) || (status == vogl_gl_replayer::cStatusResizeWindow) || (status == vogl_gl_replayer::cStatusAtEOF) || (status == vogl_gl_replayer::cStatusHardFailure)) { break; } } } if (status == vogl_gl_replayer::cStatusHardFailure) break; if (status == vogl_gl_replayer::cStatusAtEOF) { vogl_message_printf("At trace EOF, frame index %u\n", replayer.get_frame_index()); } if (replayer.get_at_frame_boundary() && pSnapshot && (loop_count > 0) && ((pTrace_reader->get_cur_frame() == snapshot_loop_end_frame) || (status == vogl_gl_replayer::cStatusAtEOF))) { status = replayer.begin_applying_snapshot(pSnapshot, false); if ((status != vogl_gl_replayer::cStatusOK) && (status != vogl_gl_replayer::cStatusResizeWindow)) goto error_exit; pTrace_reader->seek_to_frame(static_cast<uint>(snapshot_loop_start_frame)); vogl_debug_printf("Applying snapshot and seeking back to frame %" PRIi64 "\n", snapshot_loop_start_frame); loop_count--; } else { bool print_progress = (status == vogl_gl_replayer::cStatusAtEOF) || ((replayer.get_at_frame_boundary()) && ((replayer.get_frame_index() % 100) == 0)); if (print_progress) { if (pTrace_reader->get_type() == cBINARY_TRACE_FILE_READER) { vogl_binary_trace_file_reader &binary_trace_reader = *static_cast<vogl_binary_trace_file_reader *>(pTrace_reader.get()); vogl_printf("Replay now at frame index %u, trace file offet %" PRIu64 ", GL call counter %" PRIu64 ", %3.2f%% percent complete\n", replayer.get_frame_index(), binary_trace_reader.get_cur_file_ofs(), replayer.get_last_parsed_call_counter(), binary_trace_reader.get_trace_file_size() ? (binary_trace_reader.get_cur_file_ofs() * 100.0f) / binary_trace_reader.get_trace_file_size() : 0); } } if (status == vogl_gl_replayer::cStatusAtEOF) { if (!endless_mode) { double time_since_start = tm.get_elapsed_secs(); 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); break; } vogl_printf("Resetting state and rewinding back to frame 0\n"); replayer.reset_state(); if (!pTrace_reader->seek_to_frame(0)) { vogl_error_printf("Failed rewinding trace reader!\n"); goto error_exit; } } } telemetry_tick(); } normal_exit: return true; error_exit: return false; }
bool vogleditor_traceReplayer::recursive_replay_apicallTreeItem(vogleditor_apiCallTreeItem* pItem, vogleditor_gl_state_snapshot** ppNewSnapshot, uint64_t apiCallNumber) { bool bStatus = true; vogleditor_apiCallItem* pApiCall = pItem->apiCallItem(); if (pApiCall != NULL) { vogl_trace_packet* pTrace_packet = pApiCall->getTracePacket(); vogl_gl_replayer::status_t status = vogl_gl_replayer::cStatusOK; // See if a window resize or snapshot is pending. If a window resize is pending we must delay a while and pump X events until the window is resized. while (m_pTraceReplayer->get_has_pending_window_resize() || m_pTraceReplayer->get_pending_apply_snapshot()) { // Pump X events in case the window is resizing bStatus = process_x_events(); if (bStatus) { status = m_pTraceReplayer->process_pending_window_resize(); if (status != vogl_gl_replayer::cStatusResizeWindow) break; } else { // most likely the window wants to close, so let's return return false; } } // replay the trace packet if (status == vogl_gl_replayer::cStatusOK) status = m_pTraceReplayer->process_next_packet(*pTrace_packet); // if that was successful, check to see if a state snapshot is needed if ((status != vogl_gl_replayer::cStatusHardFailure) && (status != vogl_gl_replayer::cStatusAtEOF)) { if (ppNewSnapshot != NULL) { // get the snapshot after the selected api call if ((!*ppNewSnapshot) && (m_pTraceReplayer->get_last_processed_call_counter() == static_cast<int64_t>(apiCallNumber))) { vogl_printf("Taking snapshot on API call # %" PRIu64 "\n", apiCallNumber); vogl_gl_state_snapshot* pNewSnapshot = m_pTraceReplayer->snapshot_state(); if (pNewSnapshot == NULL) { vogl_error_printf("Taking new snapshot failed!\n"); } else { vogl_printf("Taking snapshot succeeded\n"); *ppNewSnapshot = vogl_new(vogleditor_gl_state_snapshot, pNewSnapshot); if (*ppNewSnapshot == NULL) { vogl_error_printf("Allocating memory for snapshot container failed!\n"); vogl_delete(pNewSnapshot); } } bStatus = false; } } } else { // replaying the trace packet failed, set as error vogl_error_printf("%s: unable to replay gl entrypoint at call %" PRIu64 "\n", VOGL_FUNCTION_NAME, pTrace_packet->get_call_counter()); bStatus = false; } } if (bStatus && pItem->has_snapshot() && pItem->get_snapshot()->is_edited() && pItem->get_snapshot()->is_valid()) { bStatus = applying_snapshot_and_process_resize(pItem->get_snapshot()->get_snapshot()); } if (bStatus) { for (int i = 0; i < pItem->childCount(); i++) { bStatus = recursive_replay_apicallTreeItem(pItem->child(i), ppNewSnapshot, apiCallNumber); if (!bStatus) break; } } return bStatus; }
//---------------------------------------------------------------------------------------------------------------------- // tool_replay_mode //---------------------------------------------------------------------------------------------------------------------- static bool tool_replay_mode() { VOGL_FUNC_TRACER dynamic_string trace_filename(g_command_line_params().get_value_as_string_or_empty("", 1)); if (trace_filename.is_empty()) { vogl_error_printf("%s: No trace file specified!\n", VOGL_FUNCTION_INFO_CSTR); return false; } dynamic_string actual_trace_filename; 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())); if (!pTrace_reader.get()) { vogl_error_printf("%s: File not found, or unable to determine file type of trace file \"%s\"\n", VOGL_FUNCTION_INFO_CSTR, trace_filename.get_ptr()); return false; } vogl_printf("Reading trace file %s\n", actual_trace_filename.get_ptr()); vogl_gl_replayer replayer; vogl_replay_window window; uint replayer_flags = get_replayer_flags_from_command_line_params(); // TODO: This will create a window with default attributes, which seems fine for the majority of traces. // 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. // Also, this design only supports a single window, which is going to be a problem with multiple window traces. 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))) { vogl_error_printf("%s: Failed initializing replay window\n", VOGL_FUNCTION_INFO_CSTR); return false; } if (!replayer.init(replayer_flags, &window, pTrace_reader->get_sof_packet(), pTrace_reader->get_multi_blob_manager())) { vogl_error_printf("%s: Failed initializing GL replayer\n", VOGL_FUNCTION_INFO_CSTR); return false; } // Disable all glGetError() calls in vogl_utils.cpp. vogl_disable_gl_get_error(); XSelectInput(window.get_display(), window.get_xwindow(), EnterWindowMask | LeaveWindowMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | ExposureMask | FocusChangeMask | KeyPressMask | KeyReleaseMask | PropertyChangeMask | StructureNotifyMask | KeymapStateMask); Atom wmDeleteMessage = XInternAtom(window.get_display(), "WM_DELETE_WINDOW", False); XSetWMProtocols(window.get_display(), window.get_xwindow(), &wmDeleteMessage, 1); // Bool win_mapped = false; vogl_gl_state_snapshot *pSnapshot = NULL; int64_t snapshot_loop_start_frame = -1; int64_t snapshot_loop_end_frame = -1; vogl::hash_map<uint64_t> keys_pressed, keys_down; int loop_frame = g_command_line_params().get_value_as_int("loop_frame", 0, -1); int loop_len = math::maximum<int>(g_command_line_params().get_value_as_int("loop_len", 0, 1), 1); int loop_count = math::maximum<int>(g_command_line_params().get_value_as_int("loop_count", 0, cINT32_MAX), 1); bool endless_mode = g_command_line_params().get_value_as_bool("endless"); timer tm; tm.start(); for (;;) { tmZone(TELEMETRY_LEVEL0, TMZF_NONE, "Main Loop"); while (X11_Pending(window.get_display())) { XEvent newEvent; // Watch for new X eventsn XNextEvent(window.get_display(), &newEvent); switch (newEvent.type) { case KeyPress: { KeySym xsym = XLookupKeysym(&newEvent.xkey, 0); //printf("KeyPress 0%04llX %" PRIu64 "\n", (uint64_t)xsym, (uint64_t)xsym); keys_down.insert(xsym); keys_pressed.insert(xsym); break; } case KeyRelease: { KeySym xsym = XLookupKeysym(&newEvent.xkey, 0); //printf("KeyRelease 0x%04llX %" PRIu64 "\n", (uint64_t)xsym, (uint64_t)xsym); keys_down.erase(xsym); break; } case FocusIn: case FocusOut: { //printf("FocusIn/FocusOut\n"); keys_down.reset(); break; } case MappingNotify: { //XRefreshKeyboardMapping(&newEvent); break; } case UnmapNotify: { // printf("UnmapNotify\n"); // win_mapped = false; keys_down.reset(); break; } case MapNotify: { // printf("MapNotify\n"); // win_mapped = true; keys_down.reset(); if (!replayer.update_window_dimensions()) goto error_exit; break; } case ConfigureNotify: { if (!replayer.update_window_dimensions()) goto error_exit; break; } case DestroyNotify: { vogl_message_printf("Exiting\n"); goto normal_exit; } case ClientMessage: { if (newEvent.xclient.data.l[0] == (int)wmDeleteMessage) { vogl_message_printf("Exiting\n"); goto normal_exit; } break; } default: break; } } if (replayer.get_at_frame_boundary()) { if ((!pSnapshot) && (loop_frame != -1) && (static_cast<int64_t>(replayer.get_frame_index()) == loop_frame)) { vogl_debug_printf("%s: Capturing replayer state at start of frame %u\n", VOGL_FUNCTION_INFO_CSTR, replayer.get_frame_index()); pSnapshot = replayer.snapshot_state(); if (pSnapshot) { vogl_printf("Snapshot succeeded\n"); snapshot_loop_start_frame = pTrace_reader->get_cur_frame(); snapshot_loop_end_frame = pTrace_reader->get_cur_frame() + loop_len; vogl_debug_printf("%s: Loop start: %" PRIi64 " Loop end: %" PRIi64 "\n", VOGL_FUNCTION_INFO_CSTR, snapshot_loop_start_frame, snapshot_loop_end_frame); } else { vogl_error_printf("Snapshot failed!\n"); loop_frame = -1; } } } vogl_gl_replayer::status_t status = replayer.process_pending_window_resize(); if (status == vogl_gl_replayer::cStatusOK) { for (;;) { status = replayer.process_next_packet(*pTrace_reader); if ((status == vogl_gl_replayer::cStatusNextFrame) || (status == vogl_gl_replayer::cStatusResizeWindow) || (status == vogl_gl_replayer::cStatusAtEOF) || (status == vogl_gl_replayer::cStatusHardFailure)) { break; } } } if (status == vogl_gl_replayer::cStatusHardFailure) break; if (status == vogl_gl_replayer::cStatusAtEOF) { vogl_message_printf("%s: At trace EOF, frame index %u\n", VOGL_FUNCTION_INFO_CSTR, replayer.get_frame_index()); } if (replayer.get_at_frame_boundary() && pSnapshot && (loop_count > 0) && ((pTrace_reader->get_cur_frame() == snapshot_loop_end_frame) || (status == vogl_gl_replayer::cStatusAtEOF))) { status = replayer.begin_applying_snapshot(pSnapshot, false); if ((status != vogl_gl_replayer::cStatusOK) && (status != vogl_gl_replayer::cStatusResizeWindow)) goto error_exit; pTrace_reader->seek_to_frame(static_cast<uint>(snapshot_loop_start_frame)); vogl_debug_printf("%s: Applying snapshot and seeking back to frame %" PRIi64 "\n", VOGL_FUNCTION_INFO_CSTR, snapshot_loop_start_frame); loop_count--; } else { bool print_progress = (status == vogl_gl_replayer::cStatusAtEOF) || ((replayer.get_at_frame_boundary()) && ((replayer.get_frame_index() % 100) == 0)); if (print_progress) { if (pTrace_reader->get_type() == cBINARY_TRACE_FILE_READER) { vogl_binary_trace_file_reader &binary_trace_reader = *static_cast<vogl_binary_trace_file_reader *>(pTrace_reader.get()); vogl_printf("Replay now at frame index %u, trace file offet %" PRIu64 ", GL call counter %" PRIu64 ", %3.2f%% percent complete\n", replayer.get_frame_index(), binary_trace_reader.get_cur_file_ofs(), replayer.get_last_parsed_call_counter(), binary_trace_reader.get_trace_file_size() ? (binary_trace_reader.get_cur_file_ofs() * 100.0f) / binary_trace_reader.get_trace_file_size() : 0); } } if (status == vogl_gl_replayer::cStatusAtEOF) { if (!endless_mode) { double time_since_start = tm.get_elapsed_secs(); 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); break; } vogl_printf("Resetting state and rewinding back to frame 0\n"); replayer.reset_state(); if (!pTrace_reader->seek_to_frame(0)) { vogl_error_printf("%s: Failed rewinding trace reader!\n", VOGL_FUNCTION_INFO_CSTR); goto error_exit; } } } telemetry_tick(); } normal_exit: return true; error_exit: return false; }
bool tool_trace_mode(vogl::vector<command_line_param_desc> *desc) { arguments_t args; if (desc) { desc->append(g_command_line_param_descs_dump, VOGL_ARRAY_SIZE(g_command_line_param_descs_dump)); desc->append(g_tracer_cmdline_opts, VOGL_ARRAY_SIZE(g_tracer_cmdline_opts)); return true; } // Get steam gameid / local application name. args.gameid = g_command_line_params().get_value_as_string_or_empty("", 1); if (!args.gameid.size()) errorf(VOGL_FUNCTION_INFO_CSTR, "ERROR: No application or steamid specified.\n"); // Get logfile and tracefile names. args.vogl_tracefile = g_command_line_params().get_value_as_string("vogl_tracefile"); args.vogl_logfile = g_command_line_params().get_value_as_string("vogl_logfile"); args.dryrun = g_command_line_params().get_value_as_bool("dry-run"); args.xterm = g_command_line_params().get_value_as_bool("xterm"); // Loop through all the arguments looking for "vogl_*". for (vogl::command_line_params::param_map_const_iterator param = g_command_line_params().begin(); param != g_command_line_params().end(); ++param) { const dynamic_string &first = param->first; // If this is a vogl command and it's not logfile / tracefile, add it to the vogl_cmdline. const dynamic_string prefix = param->first.get_clone().left(5); if (prefix == "vogl_" && (first != "vogl_logfile" && first != "vogl_tracefile")) { args.vogl_cmdline += " --" + first; for (uint32_t i = 0; i < param->second.m_values.size(); i++) { args.vogl_cmdline += " " + param->second.m_values[i]; } } } // Check for -- or --args and add everything after to game_args. const dynamic_string& command_line = get_command_line(); int args_index = command_line.find_left("-- "); if (args_index == -1) args_index = command_line.find_left("--args "); if (args_index != -1) { args_index = command_line.find_left(' ', args_index); if (args_index != -1) { args.game_args += command_line.get_clone().right(args_index + 1); } } bool is_steam_file = true; if (atoi(args.gameid.c_str()) == 0) { if (access(args.gameid.c_str(), F_OK)) errorf(VOGL_FUNCTION_INFO_CSTR, "\nCould not find executable '%s'\n", args.gameid.c_str()); char *filename = realpath(args.gameid.c_str(), NULL); if (filename) { // This is a local executable. is_steam_file = false; args.gameid = filename; free(filename); } } int steam_appid = is_steam_file ? atoi(args.gameid.c_str()) : -1; if (!steam_appid) errorf(VOGL_FUNCTION_INFO_CSTR, "ERROR: Could not find game number for %s\n", args.gameid.c_str()); dynamic_string gameid_str; if (is_steam_file) { gameid_str = "appid" + args.gameid; vogl_printf("\nGame AppID: %d", steam_appid); const char *game_name = get_game_name(steam_appid); if (game_name) { dynamic_string game_name_str(cVarArg, "%s", game_name); vogl_printf(" (%s)", game_name_str.c_str()); // Trim some characters that don't go well with filenames. game_name_str.replace(" ", "_"); game_name_str.replace(":", ""); game_name_str.replace("'", ""); game_name_str.replace("!", ""); game_name_str.replace("?", ""); gameid_str += "_" + game_name_str; } vogl_printf("\n"); } else { gameid_str = basename((char *)args.gameid.c_str()); vogl_printf("\nGame: %s\n", args.gameid.c_str()); } // If a tracefile / logfile wasn't specified, set em up. if (!args.vogl_tracefile.size() || !args.vogl_logfile.size()) { char timestr[200]; time_t t = time(NULL); timestr[0] = 0; struct tm *tmp = localtime(&t); if (tmp) { strftime(timestr, sizeof(timestr), "%Y_%m_%d-%H_%M_%S", tmp); } dynamic_string fname(cVarArg, "%s/vogltrace.%s.%s", P_tmpdir, gameid_str.c_str(), timestr); if (!args.vogl_tracefile.size()) args.vogl_tracefile = fname + ".bin"; if (!args.vogl_logfile.size()) args.vogl_logfile = fname + ".log"; } vogl_printf("\n"); vogl_message_printf("Tracefile: %s\n", args.vogl_tracefile.c_str()); vogl_message_printf("Logfile: %s", args.vogl_logfile.c_str()); vogl_printf(" (will have PID appended)\n"); dynamic_string vogltracepath32 = getfullpath("libvogltrace32.so"); dynamic_string vogltracepath64 = getfullpath("libvogltrace64.so"); // set up LD_PRELOAD string dynamic_string LD_PRELOAD = "LD_PRELOAD=\""; LD_PRELOAD += vogltracepath32 + ":" + vogltracepath64; if (is_steam_file || getenv("LD_PRELOAD")) LD_PRELOAD += ":$LD_PRELOAD"; LD_PRELOAD += "\" "; vogl_printf("\n%s\n", LD_PRELOAD.c_str()); // set up VOGL_CMD_LINE string dynamic_string VOGL_CMD_LINE = "VOGL_CMD_LINE=\""; VOGL_CMD_LINE += "--vogl_tracefile " + args.vogl_tracefile; VOGL_CMD_LINE += " --vogl_logfile " + args.vogl_logfile; VOGL_CMD_LINE += args.vogl_cmdline; VOGL_CMD_LINE += "\""; vogl_printf("\n%s\n", VOGL_CMD_LINE.c_str()); dynamic_string xterm_str; if (args.xterm) { xterm_str = "xterm -geom 120x80+20+20"; const char *env_user = getenv("USER"); // If this is mikesart, specify using the Consolas font (which he likes). if (env_user && !strcmp(env_user, "mikesart")) xterm_str += " -fa Consolas -fs 10"; // Add the xterm command part. xterm_str += " -e "; } if (is_steam_file) { // set up xterm string dynamic_string steam_cmd_str; steam_cmd_str = xterm_str + "%command% " + args.game_args; steam_cmd_str.trim(); // set up steam string dynamic_string steam_str = "steam steam://run/" + args.gameid + "//"; dynamic_string steam_args = VOGL_CMD_LINE + " " + LD_PRELOAD + steam_cmd_str; dynamic_string steam_fullcmd = steam_str + url_encode(steam_args.c_str()); // Spew this whole mess out. vogl_printf("\nLaunch string:\n%s\n", (steam_str + steam_args).c_str()); vogl_printf("\nURL encoded launch string:\n%s\n", steam_fullcmd.c_str()); // And launch it... if (!args.dryrun) system(steam_fullcmd.c_str()); } else { dynamic_string system_cmd; system_cmd = VOGL_CMD_LINE + " " + LD_PRELOAD + " " + xterm_str + args.gameid + " " + args.game_args; vogl_printf("\nlaunch string:\n%s\n", system_cmd.c_str()); if (!args.dryrun) system(system_cmd.c_str()); } return true; }