struct pipe_screen * ddebug_screen_create(struct pipe_screen *screen) { struct dd_screen *dscreen; const char *option; bool flush = false; bool verbose = false; bool transfers = false; unsigned timeout = 1000; unsigned apitrace_dump_call = 0; enum dd_dump_mode mode = DD_DUMP_ONLY_HANGS; option = debug_get_option("GALLIUM_DDEBUG", NULL); if (!option) return screen; if (!strcmp(option, "help")) { puts("Gallium driver debugger"); puts(""); puts("Usage:"); puts(""); puts(" GALLIUM_DDEBUG=\"[<timeout in ms>] [(always|apitrace <call#)] [flush] [transfers] [verbose]\""); puts(" GALLIUM_DDEBUG_SKIP=[count]"); puts(""); puts("Dump context and driver information of draw calls into"); puts("$HOME/"DD_DIR"/. By default, watch for GPU hangs and only dump information"); puts("about draw calls related to the hang."); puts(""); puts("<timeout in ms>"); puts(" Change the default timeout for GPU hang detection (default=1000ms)."); puts(" Setting this to 0 will disable GPU hang detection entirely."); puts(""); puts("always"); puts(" Dump information about all draw calls."); puts(""); puts("transfers"); puts(" Also dump and do hang detection on transfers."); puts(""); puts("apitrace <call#>"); puts(" Dump information about the draw call corresponding to the given"); puts(" apitrace call number and exit."); puts(""); puts("flush"); puts(" Flush after every draw call."); puts(""); puts("verbose"); puts(" Write additional information to stderr."); puts(""); puts("GALLIUM_DDEBUG_SKIP=count"); puts(" Skip dumping on the first count draw calls (only relevant with 'always')."); puts(""); exit(0); } for (;;) { skip_space(&option); if (!*option) break; if (match_word(&option, "always")) { if (mode == DD_DUMP_APITRACE_CALL) { printf("ddebug: both 'always' and 'apitrace' specified\n"); exit(1); } mode = DD_DUMP_ALL_CALLS; } else if (match_word(&option, "flush")) { flush = true; } else if (match_word(&option, "transfers")) { transfers = true; } else if (match_word(&option, "verbose")) { verbose = true; } else if (match_word(&option, "apitrace")) { if (mode != DD_DUMP_ONLY_HANGS) { printf("ddebug: 'apitrace' can only appear once and not mixed with 'always'\n"); exit(1); } if (!match_uint(&option, &apitrace_dump_call)) { printf("ddebug: expected call number after 'apitrace'\n"); exit(1); } mode = DD_DUMP_APITRACE_CALL; } else if (match_uint(&option, &timeout)) { /* no-op */ } else { printf("ddebug: bad options: %s\n", option); exit(1); } } dscreen = CALLOC_STRUCT(dd_screen); if (!dscreen) return NULL; #define SCR_INIT(_member) \ dscreen->base._member = screen->_member ? dd_screen_##_member : NULL dscreen->base.destroy = dd_screen_destroy; dscreen->base.get_name = dd_screen_get_name; dscreen->base.get_vendor = dd_screen_get_vendor; dscreen->base.get_device_vendor = dd_screen_get_device_vendor; SCR_INIT(get_disk_shader_cache); dscreen->base.get_param = dd_screen_get_param; dscreen->base.get_paramf = dd_screen_get_paramf; dscreen->base.get_compute_param = dd_screen_get_compute_param; dscreen->base.get_shader_param = dd_screen_get_shader_param; dscreen->base.query_memory_info = dd_screen_query_memory_info; /* get_video_param */ /* get_compute_param */ SCR_INIT(get_timestamp); dscreen->base.context_create = dd_screen_context_create; dscreen->base.is_format_supported = dd_screen_is_format_supported; /* is_video_format_supported */ SCR_INIT(can_create_resource); dscreen->base.resource_create = dd_screen_resource_create; dscreen->base.resource_from_handle = dd_screen_resource_from_handle; SCR_INIT(resource_from_memobj); SCR_INIT(resource_from_user_memory); SCR_INIT(check_resource_capability); dscreen->base.resource_get_handle = dd_screen_resource_get_handle; SCR_INIT(resource_changed); dscreen->base.resource_destroy = dd_screen_resource_destroy; SCR_INIT(flush_frontbuffer); SCR_INIT(fence_reference); SCR_INIT(fence_finish); SCR_INIT(memobj_create_from_handle); SCR_INIT(memobj_destroy); SCR_INIT(get_driver_query_info); SCR_INIT(get_driver_query_group_info); SCR_INIT(get_compiler_options); SCR_INIT(get_driver_uuid); SCR_INIT(get_device_uuid); #undef SCR_INIT dscreen->screen = screen; dscreen->timeout_ms = timeout; dscreen->dump_mode = mode; dscreen->flush_always = flush; dscreen->transfers = transfers; dscreen->verbose = verbose; dscreen->apitrace_dump_call = apitrace_dump_call; switch (dscreen->dump_mode) { case DD_DUMP_ALL_CALLS: fprintf(stderr, "Gallium debugger active. Logging all calls.\n"); break; case DD_DUMP_APITRACE_CALL: fprintf(stderr, "Gallium debugger active. Going to dump an apitrace call.\n"); break; default: fprintf(stderr, "Gallium debugger active.\n"); break; } if (dscreen->timeout_ms > 0) fprintf(stderr, "Hang detection timeout is %ums.\n", dscreen->timeout_ms); else fprintf(stderr, "Hang detection is disabled.\n"); dscreen->skip_count = debug_get_num_option("GALLIUM_DDEBUG_SKIP", 0); if (dscreen->skip_count > 0) { fprintf(stderr, "Gallium debugger skipping the first %u draw calls.\n", dscreen->skip_count); } return &dscreen->base; }
struct pipe_screen * ddebug_screen_create(struct pipe_screen *screen) { struct dd_screen *dscreen; const char *option = debug_get_option("GALLIUM_DDEBUG", NULL); bool dump_always = option && !strcmp(option, "always"); bool no_flush = option && strstr(option, "noflush"); bool help = option && !strcmp(option, "help"); unsigned timeout = 0; if (help) { puts("Gallium driver debugger"); puts(""); puts("Usage:"); puts(""); puts(" GALLIUM_DDEBUG=always"); puts(" Dump context and driver information after every draw call into"); puts(" $HOME/"DD_DIR"/."); puts(""); puts(" GALLIUM_DDEBUG=[timeout in ms] noflush"); puts(" Flush and detect a device hang after every draw call based on the given"); puts(" fence timeout and dump context and driver information into"); puts(" $HOME/"DD_DIR"/ when a hang is detected."); puts(" If 'noflush' is specified, only detect hangs in pipe->flush."); puts(""); puts(" GALLIUM_DDEBUG_SKIP=[count]"); puts(" Skip flush and hang detection for the given initial number of draw calls."); puts(""); exit(0); } if (!option) return screen; if (!dump_always && sscanf(option, "%u", &timeout) != 1) return screen; dscreen = CALLOC_STRUCT(dd_screen); if (!dscreen) return NULL; #define SCR_INIT(_member) \ dscreen->base._member = screen->_member ? dd_screen_##_member : NULL dscreen->base.destroy = dd_screen_destroy; dscreen->base.get_name = dd_screen_get_name; dscreen->base.get_vendor = dd_screen_get_vendor; dscreen->base.get_device_vendor = dd_screen_get_device_vendor; dscreen->base.get_param = dd_screen_get_param; dscreen->base.get_paramf = dd_screen_get_paramf; dscreen->base.get_shader_param = dd_screen_get_shader_param; /* get_video_param */ /* get_compute_param */ SCR_INIT(get_timestamp); dscreen->base.context_create = dd_screen_context_create; dscreen->base.is_format_supported = dd_screen_is_format_supported; /* is_video_format_supported */ SCR_INIT(can_create_resource); dscreen->base.resource_create = dd_screen_resource_create; dscreen->base.resource_from_handle = dd_screen_resource_from_handle; SCR_INIT(resource_from_user_memory); dscreen->base.resource_get_handle = dd_screen_resource_get_handle; dscreen->base.resource_destroy = dd_screen_resource_destroy; SCR_INIT(flush_frontbuffer); SCR_INIT(fence_reference); SCR_INIT(fence_finish); SCR_INIT(get_driver_query_info); SCR_INIT(get_driver_query_group_info); #undef SCR_INIT dscreen->screen = screen; dscreen->timeout_ms = timeout; dscreen->mode = dump_always ? DD_DUMP_ALL_CALLS : DD_DETECT_HANGS; dscreen->no_flush = no_flush; switch (dscreen->mode) { case DD_DUMP_ALL_CALLS: fprintf(stderr, "Gallium debugger active. Logging all calls.\n"); break; case DD_DETECT_HANGS: fprintf(stderr, "Gallium debugger active. " "The hang detection timout is %i ms.\n", timeout); break; default: assert(0); } dscreen->skip_count = debug_get_num_option("GALLIUM_DDEBUG_SKIP", 0); if (dscreen->skip_count > 0) { fprintf(stderr, "Gallium debugger skipping the first %u draw calls.\n", dscreen->skip_count); } return &dscreen->base; }