1#! /usr/bin/perl 2# 3# Copyright © 2017 Intel Corporation 4# 5# Permission is hereby granted, free of charge, to any person obtaining a 6# copy of this software and associated documentation files (the "Software"), 7# to deal in the Software without restriction, including without limitation 8# the rights to use, copy, modify, merge, publish, distribute, sublicense, 9# and/or sell copies of the Software, and to permit persons to whom the 10# Software is furnished to do so, subject to the following conditions: 11# 12# The above copyright notice and this permission notice (including the next 13# paragraph) shall be included in all copies or substantial portions of the 14# Software. 15# 16# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22# IN THE SOFTWARE. 23# 24 25use strict; 26use warnings; 27use 5.010; 28 29my $gid = 0; 30my (%db, %vdb, %queue, %submit, %notify, %rings, %ctxdb, %ringmap, %reqwait, 31 %ctxtimelines); 32my (%cids, %ctxmap); 33my $cid = 0; 34my %queues; 35my @freqs; 36 37use constant VENG => '255:254'; 38 39my $max_requests = 1000; 40my $width_us = 32000; 41my $correct_durations = 0; 42my %ignore_ring; 43my %skip_box; 44my $html = 0; 45my $trace = 0; 46my $avg_delay_stats = 0; 47my $gpu_timeline = 0; 48my $colour_contexts = 0; 49 50my @args; 51 52sub arg_help 53{ 54 return unless scalar(@_); 55 56 if ($_[0] eq '--help' or $_[0] eq '-h') { 57 shift @_; 58print <<ENDHELP; 59Notes: 60 61 The tool parse the output generated by the 'perf script' command after the 62 correct set of i915 tracepoints have been collected via perf record. 63 64 To collect the data: 65 66 ./trace.pl --trace [command-to-be-profiled] 67 68 The above will invoke perf record, or alternatively it can be done directly: 69 70 perf record -a -c 1 -e i915:intel_gpu_freq_change, \ 71 i915:i915_request_add, \ 72 i915:i915_request_submit, \ 73 i915:i915_request_in, \ 74 i915:i915_request_out, \ 75 dma_fence:dma_fence_signaled, \ 76 i915:i915_request_wait_begin, \ 77 i915:i915_request_wait_end \ 78 [command-to-be-profiled] 79 80 Then create the log file with: 81 82 perf script >trace.log 83 84 This file in turn should be piped into this tool which will generate some 85 statistics out of it, or if --html was given HTML output. 86 87 HTML can be viewed from a directory containing the 'vis' JavaScript module. 88 On Ubuntu this can be installed like this: 89 90 apt-get install npm 91 npm install vis 92 93Usage: 94 trace.pl <options> <input-file >output-file 95 96 --help / -h This help text 97 --max-items=num / -m num Maximum number of boxes to put on the 98 timeline. More boxes means more work for 99 the JavaScript engine in the browser. 100 --zoom-width-ms=ms / -z ms Width of the initial timeline zoom 101 --split-requests / -s Try to split out request which were 102 submitted together due coalescing in the 103 driver. May not be 100% accurate and may 104 influence the per-engine statistics so 105 use with care. 106 --ignore-ring=id / -i id Ignore ring with the numerical id when 107 parsing the log (enum intel_engine_id). 108 Can be given multiple times. 109 --skip-box=name / -x name Do not put a certain type of a box on 110 the timeline. One of: queue, ready, 111 execute and ctxsave. 112 Can be given multiple times. 113 --html Generate HTML output. 114 --trace cmd Trace the following command. 115 --avg-delay-stats Print average delay stats. 116 --gpu-timeline Draw overall GPU busy timeline. 117 --colour-contexts / -c Use different colours for different 118 context execution boxes. 119ENDHELP 120 121 exit 0; 122 } 123 124 return @_; 125} 126 127sub arg_html 128{ 129 return unless scalar(@_); 130 131 if ($_[0] eq '--html') { 132 shift @_; 133 $html = 1; 134 } 135 136 return @_; 137} 138 139sub arg_avg_delay_stats 140{ 141 return unless scalar(@_); 142 143 if ($_[0] eq '--avg-delay-stats') { 144 shift @_; 145 $avg_delay_stats = 1; 146 } 147 148 return @_; 149} 150 151sub arg_gpu_timeline 152{ 153 return unless scalar(@_); 154 155 if ($_[0] eq '--gpu-timeline') { 156 shift @_; 157 $gpu_timeline = 1; 158 } 159 160 return @_; 161} 162 163sub arg_trace 164{ 165 my @events = ( 'i915:intel_gpu_freq_change', 166 'i915:i915_request_add', 167 'i915:i915_request_submit', 168 'i915:i915_request_in', 169 'i915:i915_request_out', 170 'dma_fence:dma_fence_signaled', 171 'i915:i915_request_wait_begin', 172 'i915:i915_request_wait_end' ); 173 174 return unless scalar(@_); 175 176 if ($_[0] eq '--trace') { 177 shift @_; 178 179 unshift @_, '--'; 180 unshift @_, join(',', @events); 181 unshift @_, ('perf', 'record', '-a', '-c', '1', '-q', '-o', 'perf.data', '-e'); 182 183 exec @_; 184 } 185 186 return @_; 187} 188 189sub arg_max_requests 190{ 191 my $val; 192 193 return unless scalar(@_); 194 195 if ($_[0] eq '--max-requests' or $_[0] eq '-m') { 196 shift @_; 197 $val = shift @_; 198 } elsif ($_[0] =~ /--max-requests=(\d+)/) { 199 shift @_; 200 $val = $1; 201 } 202 203 $max_requests = int($val) if defined $val; 204 205 return @_; 206} 207 208sub arg_zoom_width 209{ 210 my $val; 211 212 return unless scalar(@_); 213 214 if ($_[0] eq '--zoom-width-ms' or $_[0] eq '-z') { 215 shift @_; 216 $val = shift @_; 217 } elsif ($_[0] =~ /--zoom-width-ms=(\d+)/) { 218 shift @_; 219 $val = $1; 220 } 221 222 $width_us = int($val) * 1000 if defined $val; 223 224 return @_; 225} 226 227sub arg_split_requests 228{ 229 return unless scalar(@_); 230 231 if ($_[0] eq '--split-requests' or $_[0] eq '-s') { 232 shift @_; 233 $correct_durations = 1; 234 } 235 236 return @_; 237} 238 239sub arg_ignore_ring 240{ 241 my $val; 242 243 return unless scalar(@_); 244 245 if ($_[0] eq '--ignore-ring' or $_[0] eq '-i') { 246 shift @_; 247 $val = shift @_; 248 } elsif ($_[0] =~ /--ignore-ring=(\d+)/) { 249 shift @_; 250 $val = $1; 251 } 252 253 $ignore_ring{$val} = 1 if defined $val; 254 255 return @_; 256} 257 258sub arg_skip_box 259{ 260 my $val; 261 262 return unless scalar(@_); 263 264 if ($_[0] eq '--skip-box' or $_[0] eq '-x') { 265 shift @_; 266 $val = shift @_; 267 } elsif ($_[0] =~ /--skip-box=(\d+)/) { 268 shift @_; 269 $val = $1; 270 } 271 272 $skip_box{$val} = 1 if defined $val; 273 274 return @_; 275} 276 277sub arg_colour_contexts 278{ 279 return unless scalar(@_); 280 281 if ($_[0] eq '--colour-contexts' or 282 $_[0] eq '--color-contexts' or 283 $_[0] eq '-c') { 284 shift @_; 285 $colour_contexts = 1; 286 } 287 288 return @_; 289} 290 291@args = @ARGV; 292while (@args) { 293 my $left = scalar(@args); 294 295 @args = arg_help(@args); 296 @args = arg_html(@args); 297 @args = arg_avg_delay_stats(@args); 298 @args = arg_gpu_timeline(@args); 299 @args = arg_trace(@args); 300 @args = arg_max_requests(@args); 301 @args = arg_zoom_width(@args); 302 @args = arg_split_requests(@args); 303 @args = arg_ignore_ring(@args); 304 @args = arg_skip_box(@args); 305 @args = arg_colour_contexts(@args); 306 307 last if $left == scalar(@args); 308} 309 310die if scalar(@args); 311 312@ARGV = @args; 313 314sub db_key 315{ 316 my ($ring, $ctx, $seqno) = @_; 317 318 return $ring . '/' . $ctx . '/' . $seqno; 319} 320 321sub notify_key 322{ 323 my ($ctx, $seqno) = @_; 324 325 return $ctx . '/' . $seqno; 326} 327 328sub sanitize_ctx 329{ 330 my ($ctx, $ring) = @_; 331 332 if (exists $ctxdb{$ctx} and $ctxdb{$ctx} > 1) { 333 return $ctx . '.' . $ctxdb{$ctx}; 334 } else { 335 return $ctx; 336 } 337} 338 339sub is_veng 340{ 341 my ($class, $instance) = split ':', shift; 342 343 return $instance eq '254'; 344} 345 346# Main input loop - parse lines and build the internal representation of the 347# trace using a hash of requests and some auxilliary data structures. 348my $prev_freq = 0; 349my $prev_freq_ts = 0; 350while (<>) { 351 my @fields; 352 my $tp_name; 353 my %tp; 354 my ($time, $ctx, $ring, $seqno, $orig_ctx, $key); 355 356 chomp; 357 @fields = split ' '; 358 359 chop $fields[3]; 360 $time = int($fields[3] * 1000000.0 + 0.5); 361 362 $tp_name = $fields[4]; 363 364 splice @fields, 0, 5; 365 366 foreach my $f (@fields) { 367 my ($k, $v); 368 369 next unless $f =~ m/=/; 370 ($k, $v) = ($`, $'); 371 $k = 'global' if $k eq 'global_seqno'; 372 chop $v if substr($v, -1, 1) eq ','; 373 $tp{$k} = $v; 374 375 $tp{'ring'} = $tp{'engine'} if $k eq 'engine'; 376 } 377 378 next if exists $tp{'ring'} and exists $ignore_ring{$tp{'ring'}}; 379 380 if (exists $tp{'ring'} and exists $tp{'seqno'}) { 381 $ring = $tp{'ring'}; 382 $seqno = $tp{'seqno'}; 383 384 if (exists $tp{'ctx'}) { 385 $ctx = $tp{'ctx'}; 386 $orig_ctx = $ctx; 387 $ctx = sanitize_ctx($ctx, $ring); 388 $ring = VENG if is_veng($ring); 389 $key = db_key($ring, $ctx, $seqno); 390 } 391 } 392 393 if ($tp_name eq 'i915:i915_request_wait_begin:') { 394 my %rw; 395 396 next if exists $reqwait{$key}; 397 die if $ring eq VENG and not exists $queues{$ctx}; 398 399 $rw{'key'} = $key; 400 $rw{'ring'} = $ring; 401 $rw{'seqno'} = $seqno; 402 $rw{'ctx'} = $ctx; 403 $rw{'start'} = $time; 404 $reqwait{$key} = \%rw; 405 } elsif ($tp_name eq 'i915:i915_request_wait_end:') { 406 die if $ring eq VENG and not exists $queues{$ctx}; 407 408 if (exists $reqwait{$key}) { 409 $reqwait{$key}->{'end'} = $time; 410 } else { # Virtual engine 411 my $vkey = db_key(VENG, $ctx, $seqno); 412 413 die unless exists $reqwait{$vkey}; 414 415 # If the wait started on the virtual engine, attribute 416 # it to it completely. 417 $reqwait{$vkey}->{'end'} = $time; 418 } 419 } elsif ($tp_name eq 'i915:i915_request_add:') { 420 if (exists $queue{$key}) { 421 $ctxdb{$orig_ctx}++; 422 $ctx = sanitize_ctx($orig_ctx, $ring); 423 $key = db_key($ring, $ctx, $seqno); 424 } else { 425 $ctxdb{$orig_ctx} = 1; 426 } 427 428 $queue{$key} = $time; 429 if ($ring eq VENG and not exists $queues{$ctx}) { 430 $queues{$ctx} = 1 ; 431 $cids{$ctx} = $cid++; 432 $ctxmap{$cids{$ctx}} = $ctx; 433 } 434 } elsif ($tp_name eq 'i915:i915_request_submit:') { 435 die if exists $submit{$key}; 436 die unless exists $queue{$key}; 437 die if $ring eq VENG and not exists $queues{$ctx}; 438 439 $submit{$key} = $time; 440 } elsif ($tp_name eq 'i915:i915_request_in:') { 441 my ($q, $s); 442 my %req; 443 444 # preemption 445 delete $db{$key} if exists $db{$key}; 446 447 unless (exists $queue{$key}) { 448 # Virtual engine 449 my $vkey = db_key(VENG, $ctx, $seqno); 450 my %req; 451 452 die unless exists $queues{$ctx}; 453 die unless exists $queue{$vkey}; 454 die unless exists $submit{$vkey}; 455 456 # Create separate request record on the queue timeline 457 $q = $queue{$vkey}; 458 $s = $submit{$vkey}; 459 $req{'queue'} = $q; 460 $req{'submit'} = $s; 461 $req{'start'} = $time; 462 $req{'end'} = $time; 463 $req{'ring'} = VENG; 464 $req{'seqno'} = $seqno; 465 $req{'ctx'} = $ctx; 466 $req{'name'} = $ctx . '/' . $seqno; 467 $req{'global'} = $tp{'global'}; 468 $req{'port'} = $tp{'port'}; 469 470 $vdb{$vkey} = \%req; 471 } else { 472 $q = $queue{$key}; 473 $s = $submit{$key}; 474 } 475 476 $req{'start'} = $time; 477 $req{'ring'} = $ring; 478 $req{'seqno'} = $seqno; 479 $req{'ctx'} = $ctx; 480 $ctxtimelines{$ctx . '/' . $ring} = 1; 481 $req{'name'} = $ctx . '/' . $seqno; 482 $req{'global'} = $tp{'global'}; 483 $req{'port'} = $tp{'port'}; 484 $req{'queue'} = $q; 485 $req{'submit'} = $s; 486 $req{'virtual'} = 1 if exists $queues{$ctx}; 487 $rings{$ring} = $gid++ unless exists $rings{$ring}; 488 $ringmap{$rings{$ring}} = $ring; 489 $db{$key} = \%req; 490 } elsif ($tp_name eq 'i915:i915_request_out:') { 491 if ($tp{'completed?'}) { 492 my $nkey; 493 494 die unless exists $db{$key}; 495 die unless exists $db{$key}->{'start'}; 496 die if exists $db{$key}->{'end'}; 497 498 $nkey = notify_key($ctx, $seqno); 499 500 $db{$key}->{'end'} = $time; 501 $db{$key}->{'notify'} = $notify{$nkey} 502 if exists $notify{$nkey}; 503 } else { 504 delete $db{$key}; 505 } 506 } elsif ($tp_name eq 'dma_fence:dma_fence_signaled:') { 507 my $nkey; 508 509 next unless $tp{'driver'} eq 'i915' and 510 $tp{'timeline'} eq 'signaled'; 511 512 $nkey = notify_key($tp{'context'}, $tp{'seqno'}); 513 514 die if exists $notify{$nkey}; 515 $notify{$nkey} = $time unless exists $notify{$nkey}; 516 } elsif ($tp_name eq 'i915:intel_gpu_freq_change:') { 517 push @freqs, [$prev_freq_ts, $time, $prev_freq] if $prev_freq; 518 $prev_freq_ts = $time; 519 $prev_freq = $tp{'new_freq'}; 520 } 521} 522 523# Sanitation pass to fixup up out of order notify and context complete, and to 524# find the largest seqno to be used for timeline sorting purposes. 525my $max_seqno = 0; 526foreach my $key (keys %db) { 527 my $nkey = notify_key($db{$key}->{'ctx'}, $db{$key}->{'seqno'}); 528 529 die unless exists $db{$key}->{'start'}; 530 531 $max_seqno = $db{$key}->{'seqno'} if $db{$key}->{'seqno'} > $max_seqno; 532 533 # Notify arrived after context complete? 534 $db{$key}->{'notify'} = $notify{$nkey} if not exists $db{$key}->{'notify'} 535 and exists $notify{$nkey}; 536 537 # No notify but we have end? 538 $db{$key}->{'notify'} = $db{$key}->{'end'} if exists $db{$key}->{'end'} and 539 not exists $db{$key}->{'notify'}; 540 541 # If user interrupt arrived out of order push it back to be no later 542 # than request out. 543 if (exists $db{$key}->{'end'} and exists $db{$key}->{'notify'} and 544 $db{$key}->{'notify'} > $db{$key}->{'end'}) { 545 $db{$key}->{'notify'} = $db{$key}->{'end'}; 546 } 547} 548 549my $key_count = scalar(keys %db); 550 551my %engine_timelines; 552 553sub sortStart { 554 my $as = $db{$a}->{'start'}; 555 my $bs = $db{$b}->{'start'}; 556 my $val; 557 558 $val = $as <=> $bs; 559 $val = $a cmp $b if $val == 0; 560 561 return $val; 562} 563 564sub get_engine_timeline { 565 my ($ring) = @_; 566 my @timeline; 567 568 return $engine_timelines{$ring} if exists $engine_timelines{$ring}; 569 570 @timeline = grep { $db{$_}->{'ring'} eq $ring } keys %db; 571 @timeline = sort sortStart @timeline; 572 $engine_timelines{$ring} = \@timeline; 573 574 return \@timeline; 575} 576 577# Fix up coalesced requests by ending them either when the following same 578# context request with known end ends, or when a different context starts. 579foreach my $gid (sort keys %rings) { 580 my $ring = $ringmap{$rings{$gid}}; 581 my $timeline = get_engine_timeline($ring); 582 my $last_complete = -1; 583 my $last_ctx = -1; 584 my $complete; 585 586 foreach my $pos (0..$#{$timeline}) { 587 my $key = @{$timeline}[$pos]; 588 my ($ctx, $end); 589 590 next if exists $db{$key}->{'end'}; 591 592 $db{$key}->{'no-end'} = 1; 593 $ctx = $db{$key}->{'ctx'}; 594 595 if ($pos > $last_complete or $ctx != $last_ctx) { 596 my $next = $pos; 597 598 undef $complete; 599 600 while ($next < $#{$timeline}) { 601 my $next_key = ${$timeline}[++$next]; 602 if ($ctx == $db{$next_key}->{'ctx'} and 603 exists $db{$next_key}->{'end'}) { 604 $last_ctx = $db{$next_key}->{'ctx'}; 605 $last_complete = $next; 606 $complete = $next_key; 607 last; 608 } 609 } 610 } 611 612 if (defined $complete) { 613 if ($ctx == $db{$complete}->{'ctx'}) { 614 $end = $db{$complete}->{'end'}; 615 } else { 616 $end = $db{$complete}->{'start'}; 617 } 618 } else { 619 # No next submission. Use notify if available or give up. 620 if (exists $db{$key}->{'notify'}) { 621 $end = $db{$key}->{'notify'}; 622 } else { 623 $end = $db{$key}->{'start'}; 624 $db{$key}->{'incomplete'} = 1; 625 } 626 } 627 628 unless (exists $db{$key}->{'notify'}) { 629 $db{$key}->{'notify'} = $end; 630 $db{$key}->{'no-notify'} = 1; 631 } 632 $db{$key}->{'end'} = $end; 633 $db{$key}->{'notify'} = $end if $db{$key}->{'notify'} > $end; 634 } 635} 636 637my $re_sort = 1; 638my @sorted_keys; 639 640sub maybe_sort_keys 641{ 642 if ($re_sort) { 643 @sorted_keys = sort sortStart keys %db; 644 $re_sort = 0; 645 die "Database changed size?!" unless scalar(@sorted_keys) == 646 $key_count; 647 } 648} 649 650maybe_sort_keys(); 651 652my %ctx_timelines; 653 654sub sortContext { 655 my $as = $db{$a}->{'seqno'}; 656 my $bs = $db{$b}->{'seqno'}; 657 my $val; 658 659 $val = $as <=> $bs; 660 661 die if $val == 0; 662 663 return $val; 664} 665 666sub get_ctx_timeline { 667 my ($ctx, $ring, $key) = @_; 668 my @timeline; 669 670 return $ctx_timelines{$key} if exists $ctx_timelines{$key}; 671 672 @timeline = grep { $db{$_}->{'ring'} eq $ring and 673 $db{$_}->{'ctx'} == $ctx } @sorted_keys; 674 # FIXME seqno restart 675 @timeline = sort sortContext @timeline; 676 677 $ctx_timelines{$key} = \@timeline; 678 679 return \@timeline; 680} 681 682# Split out merged batches if requested. 683if ($correct_durations) { 684 # Shift !port0 requests start time to after the previous context on the 685 # same timeline has finished. 686 foreach my $gid (sort keys %rings) { 687 my $ring = $ringmap{$rings{$gid}}; 688 my $timeline = get_engine_timeline($ring); 689 my $complete; 690 691 foreach my $pos (0..$#{$timeline}) { 692 my $key = @{$timeline}[$pos]; 693 my $prev = $complete; 694 my $pkey; 695 696 $complete = $key unless exists $db{$key}->{'no-end'}; 697 $pkey = $complete; 698 699 next if $db{$key}->{'port'} == 0; 700 701 $pkey = $prev if $complete eq $key; 702 703 die unless defined $pkey; 704 705 $db{$key}->{'start'} = $db{$pkey}->{'end'}; 706 $db{$key}->{'start'} = $db{$pkey}->{'notify'} if $db{$key}->{'start'} > $db{$key}->{'end'}; 707 708 die if $db{$key}->{'start'} > $db{$key}->{'end'}; 709 710 $re_sort = 1; 711 } 712 } 713 714 maybe_sort_keys(); 715 716 # Batch with no-end (no request_out) means it was submitted as part of 717 # coalesced context. This means it's start time should be set to the end 718 # time of a following request on this context timeline. 719 foreach my $tkey (sort keys %ctxtimelines) { 720 my ($ctx, $ring) = split '/', $tkey; 721 my $timeline = get_ctx_timeline($ctx, $ring, $tkey); 722 my $last_complete = -1; 723 my $complete; 724 725 foreach my $pos (0..$#{$timeline}) { 726 my $key = @{$timeline}[$pos]; 727 my $next_key; 728 729 next unless exists $db{$key}->{'no-end'}; 730 last if $pos == $#{$timeline}; 731 732 # Shift following request to start after the current 733 # one, but only if that wouldn't make it zero duration, 734 # which would indicate notify arrived after context 735 # complete. 736 $next_key = ${$timeline}[$pos + 1]; 737 if (exists $db{$key}->{'notify'} and 738 $db{$key}->{'notify'} < $db{$key}->{'end'}) { 739 $db{$next_key}->{'engine-start'} = $db{$next_key}->{'start'}; 740 $db{$next_key}->{'start'} = $db{$key}->{'notify'}; 741 $re_sort = 1; 742 } 743 } 744 } 745} 746 747maybe_sort_keys(); 748 749# GPU time accounting 750my (%running, %runnable, %queued, %batch_avg, %batch_total_avg, %batch_count); 751my (%submit_avg, %execute_avg, %ctxsave_avg); 752 753my $last_ts = 0; 754my $first_ts; 755my $min_ctx; 756 757foreach my $key (@sorted_keys) { 758 my $ring = $db{$key}->{'ring'}; 759 my $end = $db{$key}->{'end'}; 760 my $start = $db{$key}->{'start'}; 761 my $engine_start = $db{$key}->{'engine_start'}; 762 my $notify = $db{$key}->{'notify'}; 763 764 $first_ts = $db{$key}->{'queue'} if not defined $first_ts or $db{$key}->{'queue'} < $first_ts; 765 $last_ts = $end if $end > $last_ts; 766 $min_ctx = $db{$key}->{'ctx'} if not defined $min_ctx or 767 $db{$key}->{'ctx'} < $min_ctx; 768 769 unless (exists $db{$key}->{'no-end'}) { 770 $db{$key}->{'context-complete-delay'} = $end - $notify; 771 } else { 772 $db{$key}->{'context-complete-delay'} = 0; 773 } 774 775 $engine_start = $db{$key}->{'start'} unless defined $engine_start; 776 $db{$key}->{'execute-delay'} = $engine_start - $db{$key}->{'submit'}; 777 $db{$key}->{'submit-delay'} = $db{$key}->{'submit'} - $db{$key}->{'queue'}; 778 unless (exists $db{$key}->{'no-notify'}) { 779 $db{$key}->{'duration'} = $notify - $start; 780 } else { 781 $db{$key}->{'duration'} = 0; 782 } 783 784 $running{$ring} += $end - $start if $correct_durations or 785 not exists $db{$key}->{'no-end'}; 786 unless (exists $db{$key}->{'virtual'}) { 787 $runnable{$ring} += $db{$key}->{'execute-delay'}; 788 $queued{$ring} += $start - $db{$key}->{'execute-delay'} - $db{$key}->{'queue'}; 789 } 790 791 $batch_count{$ring}++; 792 793 $batch_avg{$ring} += $db{$key}->{'duration'}; 794 $batch_total_avg{$ring} += $end - $start; 795 796 $submit_avg{$ring} += $db{$key}->{'submit-delay'}; 797 $execute_avg{$ring} += $db{$key}->{'execute-delay'}; 798 $ctxsave_avg{$ring} += $db{$key}->{'context-complete-delay'}; 799} 800 801foreach my $ring (sort keys %batch_avg) { 802 $batch_avg{$ring} /= $batch_count{$ring}; 803 $batch_total_avg{$ring} /= $batch_count{$ring}; 804 $submit_avg{$ring} /= $batch_count{$ring}; 805 $execute_avg{$ring} /= $batch_count{$ring}; 806 $ctxsave_avg{$ring} /= $batch_count{$ring}; 807} 808 809# Calculate engine idle time 810my %flat_busy; 811foreach my $gid (sort keys %rings) { 812 my $ring = $ringmap{$rings{$gid}}; 813 my (@s_, @e_); 814 815 # Extract all GPU busy intervals and sort them. 816 foreach my $key (@sorted_keys) { 817 next unless $db{$key}->{'ring'} eq $ring; 818 die if $db{$key}->{'start'} > $db{$key}->{'end'}; 819 push @s_, $db{$key}->{'start'}; 820 push @e_, $db{$key}->{'end'}; 821 } 822 823 die unless $#s_ == $#e_; 824 825 # Flatten the intervals. 826 for my $i (1..$#s_) { 827 last if $i >= @s_; # End of array. 828 die if $e_[$i] < $s_[$i]; 829 if ($s_[$i] <= $e_[$i - 1]) { 830 # Current entry overlaps with the previous one. We need 831 # to merge end of the previous interval from the list 832 # with the start of the current one. 833 if ($e_[$i] >= $e_[$i - 1]) { 834 splice @e_, $i - 1, 1; 835 } else { 836 splice @e_, $i, 1; 837 } 838 splice @s_, $i, 1; 839 # Continue with the same element when list got squashed. 840 redo; 841 } 842 } 843 844 # Add up all busy times. 845 my $total = 0; 846 for my $i (0..$#s_) { 847 die if $e_[$i] < $s_[$i]; 848 849 $total = $total + ($e_[$i] - $s_[$i]); 850 } 851 852 $flat_busy{$ring} = $total; 853} 854 855# Calculate overall GPU idle time 856my @gpu_intervals; 857my (@s_, @e_); 858 859# Extract all GPU busy intervals and sort them. 860foreach my $key (@sorted_keys) { 861 push @s_, $db{$key}->{'start'}; 862 push @e_, $db{$key}->{'end'}; 863 die if $db{$key}->{'start'} > $db{$key}->{'end'}; 864} 865 866die unless $#s_ == $#e_; 867 868# Flatten the intervals (copy & paste of the flattening loop above) 869for my $i (1..$#s_) { 870 last if $i >= @s_; 871 die if $e_[$i] < $s_[$i]; 872 die if $s_[$i] < $s_[$i - 1]; 873 if ($s_[$i] <= $e_[$i - 1]) { 874 if ($e_[$i] >= $e_[$i - 1]) { 875 splice @e_, $i - 1, 1; 876 } else { 877 splice @e_, $i, 1; 878 } 879 splice @s_, $i, 1; 880 redo; 881 } 882} 883 884# Add up all busy times. 885my $total = 0; 886for my $i (0..$#s_) { 887 die if $e_[$i] < $s_[$i]; 888 889 $total = $total + ($e_[$i] - $s_[$i]); 890} 891 892# Generate data for the GPU timeline if requested 893if ($gpu_timeline) { 894 for my $i (0..$#s_) { 895 push @gpu_intervals, [ $s_[$i], $e_[$i] ]; 896 } 897} 898 899$flat_busy{'gpu-busy'} = $total / ($last_ts - $first_ts) * 100.0; 900$flat_busy{'gpu-idle'} = (1.0 - $total / ($last_ts - $first_ts)) * 100.0; 901 902# Add up all request waits per engine 903my %reqw; 904foreach my $key (keys %reqwait) { 905 $reqw{$reqwait{$key}->{'ring'}} += $reqwait{$key}->{'end'} - $reqwait{$key}->{'start'}; 906} 907 908# Add up all request waits per virtual engine 909my %vreqw; 910foreach my $key (keys %reqwait) { 911 $vreqw{$reqwait{$key}->{'ctx'}} += $reqwait{$key}->{'end'} - $reqwait{$key}->{'start'}; 912} 913 914say sprintf('GPU: %.2f%% idle, %.2f%% busy', 915 $flat_busy{'gpu-idle'}, $flat_busy{'gpu-busy'}) unless $html; 916 917my $timeline_text = $colour_contexts ? 918 'per context coloured shading like' : 'box shading like'; 919 920my %ctx_colours; 921my $ctx_table; 922 923sub generate_ctx_table 924{ 925 my @states = ('queue', 'ready', 'execute', 'ctxsave', 'incomplete'); 926 my $max_show = 6; 927 my (@ctxts, @disp_ctxts); 928 my $step; 929 930 if( $colour_contexts ) { 931 @ctxts = sort keys %ctxdb; 932 } else { 933 @ctxts = ($min_ctx); 934 } 935 936 # Limit number of shown context examples 937 $step = int(scalar(@ctxts) / $max_show); 938 if ($step) { 939 foreach my $i (0..$#ctxts) { 940 push @disp_ctxts, $ctxts[$i] unless $i % $step; 941 last if scalar(@disp_ctxts) == $max_show; 942 } 943 } else { 944 @disp_ctxts = @ctxts; 945 } 946 947 $ctx_table .= '<table>'; 948 949 foreach my $ctx (@disp_ctxts) { 950 $ctx_table .= "<tr>\n"; 951 $ctx_table .= " <td>Context $ctx</td>\n" if $colour_contexts; 952 foreach my $state (@states) { 953 $ctx_table .= " <td align='center' valign='middle'><div style='" . box_style($ctx, $state) . " padding-top: 6px; padding-bottom: 6px; padding-left: 6x; padding-right: 6px;'>" . uc($state) . "</div></td>\n"; 954 } 955 $ctx_table .= "</tr>\n"; 956 } 957 958 $ctx_table .= '</table>'; 959} 960 961sub generate_ctx_colours 962{ 963 my $num_ctx = keys %ctxdb; 964 my $i = 0; 965 966 foreach my $ctx (sort keys %ctxdb) { 967 $ctx_colours{$ctx} = int(360 / $num_ctx * $i++); 968 } 969} 970 971 972generate_ctx_colours() if $html and $colour_contexts; 973generate_ctx_table() if $html; 974 975print <<ENDHTML if $html; 976<!DOCTYPE HTML> 977<html> 978<head> 979 <title>i915 GT timeline</title> 980 981 <style type="text/css"> 982 body, html { 983 font-family: sans-serif; 984 } 985 </style> 986 987 <script src="node_modules/vis/dist/vis.js"></script> 988 <link href="node_modules/vis//dist/vis.css" rel="stylesheet" type="text/css" /> 989</head> 990<body> 991<p> 992<b>Timeline request view is $timeline_text:</b> 993<table> 994<tr> 995<td> 996$ctx_table 997</td> 998<td> 999QUEUE = requests executing on the GPU<br> 1000READY = runnable requests waiting for a slot on GPU<br> 1001EXECUTE = requests waiting on fences and dependencies before they are runnable<br> 1002CTXSAVE = GPU saving the context image<br> 1003INCOMPLETE = request of unknown completion time 1004<p> 1005Boxes are in format 'ctx-id/seqno'. 1006</p> 1007<p> 1008Use Ctrl+scroll-action to zoom-in/out and scroll-action or dragging to move around the timeline. 1009</p> 1010<button onclick="toggleStacking()">Toggle overlap stacking</button> 1011</td> 1012</tr> 1013</table> 1014<p> 1015<b>GPU idle: $flat_busy{'gpu-idle'}%</b> 1016<br> 1017<b>GPU busy: $flat_busy{'gpu-busy'}%</b> 1018</p> 1019<div id="visualization"></div> 1020 1021<script type="text/javascript"> 1022 var container = document.getElementById('visualization'); 1023 1024 var groups = new vis.DataSet([ 1025ENDHTML 1026 1027# var groups = new vis.DataSet([ 1028# {id: 1, content: 'g0'}, 1029# {id: 2, content: 'g1'} 1030# ]); 1031 1032sub html_stats 1033{ 1034 my ($stats, $group, $id) = @_; 1035 my $veng = exists $stats->{'virtual'} ? 1 : 0; 1036 my $name; 1037 1038 $name = $veng ? 'Virtual' : 'Ring'; 1039 $name .= $group; 1040 $name .= '<br><small><br>'; 1041 unless ($veng) { 1042 $name .= sprintf('%.2f', $stats->{'idle'}) . '% idle<br><br>'; 1043 $name .= sprintf('%.2f', $stats->{'busy'}) . '% busy<br>'; 1044 } 1045 $name .= sprintf('%.2f', $stats->{'runnable'}) . '% runnable<br>'; 1046 $name .= sprintf('%.2f', $stats->{'queued'}) . '% queued<br><br>'; 1047 $name .= sprintf('%.2f', $stats->{'wait'}) . '% wait<br><br>'; 1048 $name .= $stats->{'count'} . ' batches<br>'; 1049 unless ($veng) { 1050 $name .= sprintf('%.2f', $stats->{'avg'}) . 'us avg batch<br>'; 1051 $name .= sprintf('%.2f', $stats->{'total-avg'}) . 'us avg engine batch<br>'; 1052 } 1053 $name .= '</small>'; 1054 1055 print "\t{id: $id, content: '$name'},\n"; 1056} 1057 1058sub stdio_stats 1059{ 1060 my ($stats, $group, $id) = @_; 1061 my $veng = exists $stats->{'virtual'} ? 1 : 0; 1062 my $str; 1063 1064 $str = $veng ? 'Virtual' : 'Ring'; 1065 $str .= $group . ': '; 1066 $str .= $stats->{'count'} . ' batches, '; 1067 unless ($veng) { 1068 $str .= sprintf('%.2f (%.2f) avg batch us, ', 1069 $stats->{'avg'}, $stats->{'total-avg'}); 1070 $str .= sprintf('%.2f', $stats->{'idle'}) . '% idle, '; 1071 $str .= sprintf('%.2f', $stats->{'busy'}) . '% busy, '; 1072 } 1073 1074 $str .= sprintf('%.2f', $stats->{'runnable'}) . '% runnable, '; 1075 $str .= sprintf('%.2f', $stats->{'queued'}) . '% queued, '; 1076 $str .= sprintf('%.2f', $stats->{'wait'}) . '% wait'; 1077 1078 if ($avg_delay_stats and not $veng) { 1079 $str .= ', submit/execute/save-avg=('; 1080 $str .= sprintf('%.2f/%.2f/%.2f)', $stats->{'submit'}, $stats->{'execute'}, $stats->{'save'}); 1081 } 1082 1083 say $str; 1084} 1085 1086print "\t{id: 0, content: 'Freq'},\n" if $html; 1087print "\t{id: 1, content: 'GPU'},\n" if $gpu_timeline; 1088 1089my $engine_start_id = $gpu_timeline ? 2 : 1; 1090 1091foreach my $group (sort keys %rings) { 1092 my $name; 1093 my $ring = $ringmap{$rings{$group}}; 1094 my $id = $engine_start_id + $rings{$group}; 1095 my $elapsed = $last_ts - $first_ts; 1096 my %stats; 1097 1098 $stats{'idle'} = (1.0 - $flat_busy{$ring} / $elapsed) * 100.0; 1099 $stats{'busy'} = $running{$ring} / $elapsed * 100.0; 1100 if (exists $runnable{$ring}) { 1101 $stats{'runnable'} = $runnable{$ring} / $elapsed * 100.0; 1102 } else { 1103 $stats{'runnable'} = 0; 1104 } 1105 if (exists $queued{$ring}) { 1106 $stats{'queued'} = $queued{$ring} / $elapsed * 100.0; 1107 } else { 1108 $stats{'queued'} = 0; 1109 } 1110 $reqw{$ring} = 0 unless exists $reqw{$ring}; 1111 $stats{'wait'} = $reqw{$ring} / $elapsed * 100.0; 1112 $stats{'count'} = $batch_count{$ring}; 1113 $stats{'avg'} = $batch_avg{$ring}; 1114 $stats{'total-avg'} = $batch_total_avg{$ring}; 1115 $stats{'submit'} = $submit_avg{$ring}; 1116 $stats{'execute'} = $execute_avg{$ring}; 1117 $stats{'save'} = $ctxsave_avg{$ring}; 1118 1119 if ($html) { 1120 html_stats(\%stats, $group, $id); 1121 } else { 1122 stdio_stats(\%stats, $group, $id); 1123 } 1124} 1125 1126sub sortVQueue { 1127 my $as = $vdb{$a}->{'queue'}; 1128 my $bs = $vdb{$b}->{'queue'}; 1129 my $val; 1130 1131 $val = $as <=> $bs; 1132 $val = $a cmp $b if $val == 0; 1133 1134 return $val; 1135} 1136 1137my @sorted_vkeys = sort sortVQueue keys %vdb; 1138my (%vqueued, %vrunnable); 1139 1140foreach my $key (@sorted_vkeys) { 1141 my $ctx = $vdb{$key}->{'ctx'}; 1142 1143 $vdb{$key}->{'submit-delay'} = $vdb{$key}->{'submit'} - $vdb{$key}->{'queue'}; 1144 $vdb{$key}->{'execute-delay'} = $vdb{$key}->{'start'} - $vdb{$key}->{'submit'}; 1145 1146 $vqueued{$ctx} += $vdb{$key}->{'submit-delay'}; 1147 $vrunnable{$ctx} += $vdb{$key}->{'execute-delay'}; 1148} 1149 1150my $veng_id = $engine_start_id + scalar(keys %rings); 1151 1152foreach my $cid (sort keys %ctxmap) { 1153 my $ctx = $ctxmap{$cid}; 1154 my $elapsed = $last_ts - $first_ts; 1155 my %stats; 1156 1157 $stats{'virtual'} = 1; 1158 if (exists $vrunnable{$ctx}) { 1159 $stats{'runnable'} = $vrunnable{$ctx} / $elapsed * 100.0; 1160 } else { 1161 $stats{'runnable'} = 0; 1162 } 1163 if (exists $vqueued{$ctx}) { 1164 $stats{'queued'} = $vqueued{$ctx} / $elapsed * 100.0; 1165 } else { 1166 $stats{'queued'} = 0; 1167 } 1168 $vreqw{$ctx} = 0 unless exists $vreqw{$ctx}; 1169 $stats{'wait'} = $vreqw{$ctx} / $elapsed * 100.0; 1170 $stats{'count'} = scalar(grep {$ctx == $vdb{$_}->{'ctx'}} keys %vdb); 1171 1172 if ($html) { 1173 html_stats(\%stats, $cid, $veng_id++); 1174 } else { 1175 stdio_stats(\%stats, $cid, $veng_id++); 1176 } 1177} 1178 1179exit 0 unless $html; 1180 1181print <<ENDHTML; 1182 ]); 1183 1184 var items = new vis.DataSet([ 1185ENDHTML 1186 1187sub sortQueue { 1188 my $as = $db{$a}->{'queue'}; 1189 my $bs = $db{$b}->{'queue'}; 1190 my $val; 1191 1192 $val = $as <=> $bs; 1193 $val = $a cmp $b if $val == 0; 1194 1195 return $val; 1196} 1197 1198sub ctx_colour 1199{ 1200 my ($ctx, $stage, $lfac) = (@_); 1201 my ($s, $l); 1202 my $val; 1203 1204 unless ($colour_contexts) { 1205 if ($stage eq 'queue') { 1206 $val = 210; 1207 $s = 65; 1208 $l = 52; 1209 } elsif ($stage eq 'ready') { 1210 $val = 0; 1211 $s = 0; 1212 $l = 47; 1213 } elsif ($stage eq 'execute') { 1214 $val = 346; 1215 $s = 68; 1216 $l = 65; 1217 } elsif ($stage eq 'ctxsave') { 1218 $val = 26; 1219 $s = 90; 1220 $l = 52; 1221 } elsif ($stage eq 'incomplete') { 1222 $val = 0; 1223 $s = 85; 1224 $l = 50; 1225 } 1226 } else { 1227 if ($stage eq 'queue') { 1228 $s = 35; 1229 $l = 85; 1230 } elsif ($stage eq 'ready') { 1231 $s = 35; 1232 $l = 45; 1233 } elsif ($stage eq 'execute') { 1234 $s = 80; 1235 $l = 65; 1236 } elsif ($stage eq 'ctxsave') { 1237 $s = 75; 1238 $l = 70; 1239 } elsif ($stage eq 'incomplete') { 1240 $s = 80; 1241 $l = 25; 1242 } 1243 1244 $val = $ctx_colours{$ctx}; 1245 } 1246 1247 $l = int($l * $lfac); 1248 1249 return "hsl($val, $s%, $l%)"; 1250} 1251 1252sub box_style 1253{ 1254 my ($ctx, $stage) = @_; 1255 my $deg; 1256 my $text_col = 'white'; 1257 1258 if ($stage eq 'queue') { 1259 $deg = 90; 1260 $text_col = 'black' if $colour_contexts; 1261 } elsif ($stage eq 'ready') { 1262 $deg = 45; 1263 } elsif ($stage eq 'execute') { 1264 $deg = 0; 1265 $text_col = 'black' if $colour_contexts; 1266 } elsif ($stage eq 'ctxsave') { 1267 $deg = 105; 1268 $text_col = 'black' if $colour_contexts; 1269 } elsif ($stage eq 'incomplete') { 1270 $deg = 0; 1271 } 1272 1273 return "color: $text_col; background: repeating-linear-gradient(" . 1274 $deg . 'deg, ' . 1275 ctx_colour($ctx, $stage, 1.0) . ', ' . 1276 ctx_colour($ctx, $stage, 1.0) . ' 10px, ' . 1277 ctx_colour($ctx, $stage, 0.90) . ' 10px, ' . 1278 ctx_colour($ctx, $stage, 0.90) . ' 20px);'; 1279} 1280 1281my $i = 0; 1282my $req = 0; 1283foreach my $key (sort sortQueue keys %db) { 1284 my ($name, $ctx, $seqno) = ($db{$key}->{'name'}, $db{$key}->{'ctx'}, $db{$key}->{'seqno'}); 1285 my ($queue, $start, $notify, $end) = ($db{$key}->{'queue'}, $db{$key}->{'start'}, $db{$key}->{'notify'}, $db{$key}->{'end'}); 1286 my $engine_start = $db{$key}->{'engine-start'}; 1287 my $submit = $queue + $db{$key}->{'submit-delay'}; 1288 my ($content, $style); 1289 my $group = $engine_start_id + $rings{$db{$key}->{'ring'}}; 1290 my $subgroup = $ctx - $min_ctx; 1291 my $type = ' type: \'range\','; 1292 my $startend; 1293 my $skey; 1294 1295 # submit to execute 1296 unless (exists $skip_box{'queue'} or exists $db{$key}->{'virtual'}) { 1297 $skey = 2 * $max_seqno * $ctx + 2 * $seqno; 1298 $style = box_style($ctx, 'queue'); 1299 $content = "$name<br>$db{$key}->{'submit-delay'}us <small>($db{$key}->{'execute-delay'}us)</small>"; 1300 $startend = 'start: ' . $queue . ', end: ' . $submit; 1301 print "\t{id: $i, key: $skey, $type group: $group, subgroup: $subgroup, subgroupOrder: $subgroup, content: '$content', $startend, style: \'$style\'},\n"; 1302 $i++; 1303 } 1304 1305 # execute to start 1306 $engine_start = $db{$key}->{'start'} unless defined $engine_start; 1307 unless (exists $skip_box{'ready'} or exists $db{$key}->{'virtual'}) { 1308 $skey = 2 * $max_seqno * $ctx + 2 * $seqno + 1; 1309 $style = box_style($ctx, 'ready'); 1310 $content = "<small>$name<br>$db{$key}->{'execute-delay'}us</small>"; 1311 $startend = 'start: ' . $submit . ', end: ' . $engine_start; 1312 print "\t{id: $i, key: $skey, $type group: $group, subgroup: $subgroup, subgroupOrder: $subgroup, content: '$content', $startend, style: \'$style\'},\n"; 1313 $i++; 1314 } 1315 1316 # start to user interrupt 1317 unless (exists $skip_box{'execute'}) { 1318 $skey = -2 * $max_seqno * $ctx - 2 * $seqno - 1; 1319 $style = box_style($ctx, 1320 exists $db{$key}->{'incomplete'} ? 1321 'incomplete' : 'execute'); 1322 $content = "$name <small>$db{$key}->{'port'}</small>"; 1323 $content .= ' <small><i>???</i></small> ' if exists $db{$key}->{'incomplete'}; 1324 $content .= ' <small><i>++</i></small> ' if exists $db{$key}->{'no-end'}; 1325 $content .= ' <small><i>+</i></small> ' if exists $db{$key}->{'no-notify'}; 1326 $content .= "<br>$db{$key}->{'duration'}us <small>($db{$key}->{'context-complete-delay'}us)</small>"; 1327 $startend = 'start: ' . $start . ', end: ' . $notify; 1328 print "\t{id: $i, key: $skey, $type group: $group, subgroup: $subgroup, subgroupOrder: $subgroup, content: '$content', $startend, style: \'$style\'},\n"; 1329 $i++; 1330 } 1331 1332 # user interrupt to context complete 1333 unless (exists $skip_box{'ctxsave'} or exists $db{$key}->{'no-end'}) { 1334 $skey = -2 * $max_seqno * $ctx - 2 * $seqno; 1335 $style = box_style($ctx, 'ctxsave'); 1336 my $ctxsave = $db{$key}->{'end'} - $db{$key}->{'notify'}; 1337 $content = "<small>$name<br>${ctxsave}us</small>"; 1338 $content .= ' <small><i>???</i></small> ' if exists $db{$key}->{'incomplete'}; 1339 $content .= ' <small><i>++</i></small> ' if exists $db{$key}->{'no-end'}; 1340 $content .= ' <small><i>+</i></small> ' if exists $db{$key}->{'no-notify'}; 1341 $startend = 'start: ' . $notify . ', end: ' . $end; 1342 print "\t{id: $i, key: $skey, $type group: $group, subgroup: $subgroup, subgroupOrder: $subgroup, content: '$content', $startend, style: \'$style\'},\n"; 1343 $i++; 1344 } 1345 1346 $last_ts = $end; 1347 1348 last if ++$req > $max_requests; 1349} 1350 1351push @freqs, [$prev_freq_ts, $last_ts, $prev_freq] if $prev_freq; 1352 1353foreach my $item (@freqs) { 1354 my ($start, $end, $freq) = @$item; 1355 my $startend; 1356 1357 next if $start > $last_ts; 1358 1359 $start = $first_ts if $start < $first_ts; 1360 $end = $last_ts if $end > $last_ts; 1361 $startend = 'start: ' . $start . ', end: ' . $end; 1362 print "\t{id: $i, type: 'range', group: 0, content: '$freq', $startend},\n"; 1363 $i++; 1364} 1365 1366if ($gpu_timeline) { 1367 foreach my $item (@gpu_intervals) { 1368 my ($start, $end) = @$item; 1369 my $startend; 1370 1371 next if $start > $last_ts; 1372 1373 $start = $first_ts if $start < $first_ts; 1374 $end = $last_ts if $end > $last_ts; 1375 $startend = 'start: ' . $start . ', end: ' . $end; 1376 print "\t{id: $i, type: 'range', group: 1, $startend},\n"; 1377 $i++; 1378 } 1379} 1380 1381$req = 0; 1382$veng_id = $engine_start_id + scalar(keys %rings); 1383foreach my $key (@sorted_vkeys) { 1384 my ($name, $ctx, $seqno) = ($vdb{$key}->{'name'}, $vdb{$key}->{'ctx'}, $vdb{$key}->{'seqno'}); 1385 my $queue = $vdb{$key}->{'queue'}; 1386 my $submit = $vdb{$key}->{'submit'}; 1387 my $engine_start = $db{$key}->{'engine-start'}; 1388 my ($content, $style, $startend, $skey); 1389 my $group = $veng_id + $cids{$ctx}; 1390 my $subgroup = $ctx - $min_ctx; 1391 my $type = ' type: \'range\','; 1392 my $duration; 1393 1394 # submit to execute 1395 unless (exists $skip_box{'queue'}) { 1396 $skey = 2 * $max_seqno * $ctx + 2 * $seqno; 1397 $style = box_style($ctx, 'queue'); 1398 $content = "$name<br>$vdb{$key}->{'submit-delay'}us <small>($vdb{$key}->{'execute-delay'}us)</small>"; 1399 $startend = 'start: ' . $queue . ', end: ' . $submit; 1400 print "\t{id: $i, key: $skey, $type group: $group, subgroup: $subgroup, subgroupOrder: $subgroup, content: '$content', $startend, style: \'$style\'},\n"; 1401 $i++; 1402 } 1403 1404 # execute to start 1405 $engine_start = $vdb{$key}->{'start'} unless defined $engine_start; 1406 unless (exists $skip_box{'ready'}) { 1407 $skey = 2 * $max_seqno * $ctx + 2 * $seqno + 1; 1408 $style = box_style($ctx, 'ready'); 1409 $content = "<small>$name<br>$vdb{$key}->{'execute-delay'}us</small>"; 1410 $startend = 'start: ' . $submit . ', end: ' . $engine_start; 1411 print "\t{id: $i, key: $skey, $type group: $group, subgroup: $subgroup, subgroupOrder: $subgroup, content: '$content', $startend, style: \'$style\'},\n"; 1412 $i++; 1413 } 1414 1415 last if ++$req > $max_requests; 1416} 1417 1418my $end_ts = $first_ts + $width_us; 1419$first_ts = $first_ts; 1420 1421print <<ENDHTML; 1422 ]); 1423 1424 function majorAxis(date, scale, step) { 1425 var s = date / 1000000; 1426 var precision; 1427 1428 if (scale == 'millisecond') 1429 precision = 6; 1430 else if (scale == 'second') 1431 precision = 3; 1432 else 1433 precision = 0; 1434 1435 return s.toFixed(precision) + "s"; 1436 } 1437 1438 function minorAxis(date, scale, step) { 1439 var t = date; 1440 var precision; 1441 var unit; 1442 1443 if (scale == 'millisecond') { 1444 t %= 1000; 1445 precision = 0; 1446 unit = 'us'; 1447 } else if (scale == 'second') { 1448 t /= 1000; 1449 t %= 1000; 1450 precision = 0; 1451 unit = 'ms'; 1452 } else { 1453 t /= 1000000; 1454 precision = 1; 1455 unit = 's'; 1456 } 1457 1458 return t.toFixed(precision) + unit; 1459 } 1460 1461 // Configuration for the Timeline 1462 var options = { groupOrder: 'content', 1463 horizontalScroll: true, 1464 stack: false, 1465 stackSubgroups: false, 1466 zoomKey: 'ctrlKey', 1467 orientation: 'top', 1468 format: { majorLabels: majorAxis, minorLabels: minorAxis }, 1469 start: $first_ts, 1470 end: $end_ts}; 1471 1472 // Create a Timeline 1473 var timeline = new vis.Timeline(container, items, groups, options); 1474 1475 function toggleStacking() { 1476 options.stack = !options.stack; 1477 options.stackSubgroups = !options.stackSubgroups; 1478 timeline.setOptions(options); 1479 } 1480ENDHTML 1481 1482print <<ENDHTML; 1483</script> 1484</body> 1485</html> 1486ENDHTML 1487