Index: llvm/trunk/test/tools/llvm-xray/X86/Inputs/graph-diff-A.yaml =================================================================== --- llvm/trunk/test/tools/llvm-xray/X86/Inputs/graph-diff-A.yaml +++ llvm/trunk/test/tools/llvm-xray/X86/Inputs/graph-diff-A.yaml @@ -0,0 +1,29 @@ +--- +header: + version: 1 + type: 0 + constant-tsc: true + nonstop-tsc: true + cycle-frequency: 1 +records: + - { type: 0, func-id: 1, cpu: 1, thread: 111, kind: function-enter, tsc: 10000 } + - { type: 0, func-id: 1, cpu: 1, thread: 111, kind: function-exit, tsc: 10010 } + - { type: 0, func-id: 2, cpu: 1, thread: 111, kind: function-enter, tsc: 10100 } + - { type: 0, func-id: 2, cpu: 1, thread: 111, kind: function-exit, tsc: 10120 } + - { type: 0, func-id: 3, cpu: 1, thread: 111, kind: function-enter, tsc: 10200 } + - { type: 0, func-id: 3, cpu: 1, thread: 111, kind: function-exit, tsc: 10230 } + - { type: 0, func-id: 4, cpu: 1, thread: 111, kind: function-enter, tsc: 10300 } + - { type: 0, func-id: 4, cpu: 1, thread: 111, kind: function-exit, tsc: 10340 } + - { type: 0, func-id: 5, cpu: 1, thread: 111, kind: function-enter, tsc: 10400 } + - { type: 0, func-id: 5, cpu: 1, thread: 111, kind: function-exit, tsc: 10450 } + - { type: 0, func-id: 6, cpu: 1, thread: 111, kind: function-enter, tsc: 10500 } + - { type: 0, func-id: 6, cpu: 1, thread: 111, kind: function-exit, tsc: 10560 } + - { type: 0, func-id: 7, cpu: 1, thread: 111, kind: function-enter, tsc: 10600 } + - { type: 0, func-id: 7, cpu: 1, thread: 111, kind: function-exit, tsc: 10670 } + - { type: 0, func-id: 8, cpu: 1, thread: 111, kind: function-enter, tsc: 10700 } + - { type: 0, func-id: 8, cpu: 1, thread: 111, kind: function-exit, tsc: 10780 } + - { type: 0, func-id: 9, cpu: 1, thread: 111, kind: function-enter, tsc: 10800 } + - { type: 0, func-id: 9, cpu: 1, thread: 111, kind: function-exit, tsc: 10890 } + - { type: 0, func-id: 11, cpu: 1, thread: 111, kind: function-enter, tsc: 10900 } + - { type: 0, func-id: 11, cpu: 1, thread: 111, kind: function-exit, tsc: 10910 } +--- Index: llvm/trunk/test/tools/llvm-xray/X86/Inputs/graph-diff-B.yaml =================================================================== --- llvm/trunk/test/tools/llvm-xray/X86/Inputs/graph-diff-B.yaml +++ llvm/trunk/test/tools/llvm-xray/X86/Inputs/graph-diff-B.yaml @@ -0,0 +1,30 @@ +--- +header: + version: 1 + type: 0 + constant-tsc: true + nonstop-tsc: true + cycle-frequency: 1 +records: + - { type: 0, func-id: 1, cpu: 1, thread: 111, kind: function-enter, tsc: 10000 } + - { type: 0, func-id: 1, cpu: 1, thread: 111, kind: function-exit, tsc: 10090 } + - { type: 0, func-id: 2, cpu: 1, thread: 111, kind: function-enter, tsc: 10100 } + - { type: 0, func-id: 2, cpu: 1, thread: 111, kind: function-exit, tsc: 10180 } + - { type: 0, func-id: 3, cpu: 1, thread: 111, kind: function-enter, tsc: 10200 } + - { type: 0, func-id: 3, cpu: 1, thread: 111, kind: function-exit, tsc: 10270 } + - { type: 0, func-id: 4, cpu: 1, thread: 111, kind: function-enter, tsc: 10300 } + - { type: 0, func-id: 4, cpu: 1, thread: 111, kind: function-exit, tsc: 10360 } + - { type: 0, func-id: 5, cpu: 1, thread: 111, kind: function-enter, tsc: 10400 } + - { type: 0, func-id: 5, cpu: 1, thread: 111, kind: function-exit, tsc: 10450 } + - { type: 0, func-id: 6, cpu: 1, thread: 111, kind: function-enter, tsc: 10500 } + - { type: 0, func-id: 6, cpu: 1, thread: 111, kind: function-exit, tsc: 10540 } + - { type: 0, func-id: 7, cpu: 1, thread: 111, kind: function-enter, tsc: 10600 } + - { type: 0, func-id: 7, cpu: 1, thread: 111, kind: function-exit, tsc: 10630 } + - { type: 0, func-id: 8, cpu: 1, thread: 111, kind: function-enter, tsc: 10700 } + - { type: 0, func-id: 8, cpu: 1, thread: 111, kind: function-exit, tsc: 10720 } + - { type: 0, func-id: 9, cpu: 1, thread: 111, kind: function-enter, tsc: 10800 } + - { type: 0, func-id: 9, cpu: 1, thread: 111, kind: function-exit, tsc: 10810 } + - { type: 0, func-id: 10, cpu: 1, thread: 111, kind: function-enter, tsc: 10900 } + - { type: 0, func-id: 10, cpu: 1, thread: 111, kind: function-exit, tsc: 10910 } +--- + Index: llvm/trunk/test/tools/llvm-xray/X86/Inputs/simple-instrmap.yaml =================================================================== --- llvm/trunk/test/tools/llvm-xray/X86/Inputs/simple-instrmap.yaml +++ llvm/trunk/test/tools/llvm-xray/X86/Inputs/simple-instrmap.yaml @@ -19,4 +19,8 @@ - { id: 8, address: 0x9, function: 0x8, kind: function-exit, always-instrument: true} - { id: 9, address: 0x9, function: 0x9, kind: function-enter, always-instrument: true} - { id: 9, address: 0xA, function: 0x9, kind: function-exit, always-instrument: true} +- { id: 10, address: 0xA, function: 0xA, kind: function-enter, always-instrument: true} +- { id: 10, address: 0xB, function: 0xA, kind: function-exit, always-instrument: true} +- { id: 11, address: 0xB, function: 0xB, kind: function-enter, always-instrument: true} +- { id: 11, address: 0xC, function: 0xB, kind: function-exit, always-instrument: true} ... Index: llvm/trunk/test/tools/llvm-xray/X86/graph-diff-simple.txt =================================================================== --- llvm/trunk/test/tools/llvm-xray/X86/graph-diff-simple.txt +++ llvm/trunk/test/tools/llvm-xray/X86/graph-diff-simple.txt @@ -0,0 +1,238 @@ +#RUN: llvm-xray graph-diff -o - -c min -b min -m %S/Inputs/simple-instrmap.yaml \ +#RUN: %S/Inputs/graph-diff-A.yaml %S/Inputs/graph-diff-B.yaml \ +#RUN: | FileCheck %s -check-prefix=VCEC +#RUN: llvm-xray graph-diff -o - -c min -m %S/Inputs/simple-instrmap.yaml \ +#RUN: %S/Inputs/graph-diff-A.yaml %S/Inputs/graph-diff-B.yaml \ +#RUN: | FileCheck %s -check-prefix=EC +#RUN: llvm-xray graph-diff -o - -b min -m %S/Inputs/simple-instrmap.yaml \ +#RUN: %S/Inputs/graph-diff-A.yaml %S/Inputs/graph-diff-B.yaml \ +#RUN: | FileCheck %s -check-prefix=VC +#RUN: llvm-xray graph-diff -o - -e min -v min -m %S/Inputs/simple-instrmap.yaml \ +#RUN: %S/Inputs/graph-diff-A.yaml %S/Inputs/graph-diff-B.yaml \ +#RUN: | FileCheck %s -check-prefix=ELVL +#RUN: llvm-xray graph-diff -o - -e min -m %S/Inputs/simple-instrmap.yaml \ +#RUN: %S/Inputs/graph-diff-A.yaml %S/Inputs/graph-diff-B.yaml \ +#RUN: | FileCheck %s -check-prefix=EL +#RUN: llvm-xray graph-diff -o - -v min -m %S/Inputs/simple-instrmap.yaml \ +#RUN: %S/Inputs/graph-diff-A.yaml %S/Inputs/graph-diff-B.yaml \ +#RUN: | FileCheck %s -check-prefix=VL +#RUN: llvm-xray graph-diff -o - -v min -b min -m %S/Inputs/simple-instrmap.yaml \ +#RUN: %S/Inputs/graph-diff-A.yaml %S/Inputs/graph-diff-B.yaml \ +#RUN: | FileCheck %s -check-prefix=VLVC +#RUN: llvm-xray graph-diff -o - -e min -c min -m %S/Inputs/simple-instrmap.yaml \ +#RUN: %S/Inputs/graph-diff-A.yaml %S/Inputs/graph-diff-B.yaml \ +#RUN: | FileCheck %s -check-prefix=ELEC + +#VCEC: digraph xrayDiff { +#VCEC-DAG: F[[F0NO:[0-9]+]] [label="F0"] +#VCEC-DAG: F[[N1NO:[0-9]+]] [label="#1" color="#276419"] +#VCEC-DAG: F[[N2NO:[0-9]+]] [label="#2" color="#276419"] +#VCEC-DAG: F[[N3NO:[0-9]+]] [label="#3" color="#276419"] +#VCEC-DAG: F[[N4NO:[0-9]+]] [label="#4" color="#9BCF61"] +#VCEC-DAG: F[[N5NO:[0-9]+]] [label="#5" color="#F7F7F7"] +#VCEC-DAG: F[[N6NO:[0-9]+]] [label="#6" color="#F5C4E0"] +#VCEC-DAG: F[[N7NO:[0-9]+]] [label="#7" color="#E17FB4"] +#VCEC-DAG: F[[N8NO:[0-9]+]] [label="#8" color="#CB3088"] +#VCEC-DAG: F[[N9NO:[0-9]+]] [label="#9" color="#AD0E69"] +#VCEC-DAG: F[[NANO:[0-9]+]] [label="#10" color="#00FF00"] +#VCEC-DAG: F[[NBNO:[0-9]+]] [label="#11" color="#FF0000"] +#VCEC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N1NO]] [tooltip="F0 -> #1" label="" color="#276419" labelfontcolor="#276419" penwidth=8.00] +#VCEC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N2NO]] [tooltip="F0 -> #2" label="" color="#276419" labelfontcolor="#276419" penwidth=3.00] +#VCEC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N3NO]] [tooltip="F0 -> #3" label="" color="#276419" labelfontcolor="#276419" penwidth=1.33] +#VCEC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N4NO]] [tooltip="F0 -> #4" label="" color="#9BCF61" labelfontcolor="#9BCF61" penwidth=1.00] +#VCEC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N5NO]] [tooltip="F0 -> #5" label="" color="#F7F7F7" labelfontcolor="#F7F7F7" penwidth=1.00] +#VCEC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N6NO]] [tooltip="F0 -> #6" label="" color="#F5C4E0" labelfontcolor="#F5C4E0" penwidth=1.00] +#VCEC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N7NO]] [tooltip="F0 -> #7" label="" color="#E17FB4" labelfontcolor="#E17FB4" penwidth=1.00] +#VCEC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N8NO]] [tooltip="F0 -> #8" label="" color="#CB3088" labelfontcolor="#CB3088" penwidth=1.00] +#VCEC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N9NO]] [tooltip="F0 -> #9" label="" color="#AD0E69" labelfontcolor="#AD0E69" penwidth=1.00] +#VCEC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[NANO]] [tooltip="F0 -> #10" label="" color="#00FF00" labelfontcolor="#00FF00" penwidth=1.00] +#VCEC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[NBNO]] [tooltip="F0 -> #11" label="" color="#FF0000" labelfontcolor="#FF0000" penwidth=1.00] +#VCEC-NEXT:} + + +#EC: digraph xrayDiff { +#EC-DAG: F[[F0NO:[0-9]+]] [label="F0"] +#EC-DAG: F[[N1NO:[0-9]+]] [label="#1" color="black"] +#EC-DAG: F[[N2NO:[0-9]+]] [label="#2" color="black"] +#EC-DAG: F[[N3NO:[0-9]+]] [label="#3" color="black"] +#EC-DAG: F[[N4NO:[0-9]+]] [label="#4" color="black"] +#EC-DAG: F[[N5NO:[0-9]+]] [label="#5" color="black"] +#EC-DAG: F[[N6NO:[0-9]+]] [label="#6" color="black"] +#EC-DAG: F[[N7NO:[0-9]+]] [label="#7" color="black"] +#EC-DAG: F[[N8NO:[0-9]+]] [label="#8" color="black"] +#EC-DAG: F[[N9NO:[0-9]+]] [label="#9" color="black"] +#EC-DAG: F[[NANO:[0-9]+]] [label="#10" color="#00FF00"] +#EC-DAG: F[[NBNO:[0-9]+]] [label="#11" color="#FF0000"] +#EC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N1NO]] [tooltip="F0 -> #1" label="" color="#276419" labelfontcolor="#276419" penwidth=8.00] +#EC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N2NO]] [tooltip="F0 -> #2" label="" color="#276419" labelfontcolor="#276419" penwidth=3.00] +#EC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N3NO]] [tooltip="F0 -> #3" label="" color="#276419" labelfontcolor="#276419" penwidth=1.33] +#EC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N4NO]] [tooltip="F0 -> #4" label="" color="#9BCF61" labelfontcolor="#9BCF61" penwidth=1.00] +#EC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N5NO]] [tooltip="F0 -> #5" label="" color="#F7F7F7" labelfontcolor="#F7F7F7" penwidth=1.00] +#EC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N6NO]] [tooltip="F0 -> #6" label="" color="#F5C4E0" labelfontcolor="#F5C4E0" penwidth=1.00] +#EC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N7NO]] [tooltip="F0 -> #7" label="" color="#E17FB4" labelfontcolor="#E17FB4" penwidth=1.00] +#EC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N8NO]] [tooltip="F0 -> #8" label="" color="#CB3088" labelfontcolor="#CB3088" penwidth=1.00] +#EC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N9NO]] [tooltip="F0 -> #9" label="" color="#AD0E69" labelfontcolor="#AD0E69" penwidth=1.00] +#EC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[NANO]] [tooltip="F0 -> #10" label="" color="#00FF00" labelfontcolor="#00FF00" penwidth=1.00] +#EC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[NBNO]] [tooltip="F0 -> #11" label="" color="#FF0000" labelfontcolor="#FF0000" penwidth=1.00] +#EC-NEXT:} + +#VC: digraph xrayDiff { +#VC-DAG: F[[F0NO:[0-9]+]] [label="F0"] +#VC-DAG: F[[N1NO:[0-9]+]] [label="#1" color="#276419"] +#VC-DAG: F[[N2NO:[0-9]+]] [label="#2" color="#276419"] +#VC-DAG: F[[N3NO:[0-9]+]] [label="#3" color="#276419"] +#VC-DAG: F[[N4NO:[0-9]+]] [label="#4" color="#9BCF61"] +#VC-DAG: F[[N5NO:[0-9]+]] [label="#5" color="#F7F7F7"] +#VC-DAG: F[[N6NO:[0-9]+]] [label="#6" color="#F5C4E0"] +#VC-DAG: F[[N7NO:[0-9]+]] [label="#7" color="#E17FB4"] +#VC-DAG: F[[N8NO:[0-9]+]] [label="#8" color="#CB3088"] +#VC-DAG: F[[N9NO:[0-9]+]] [label="#9" color="#AD0E69"] +#VC-DAG: F[[NANO:[0-9]+]] [label="#10" color="#00FF00"] +#VC-DAG: F[[NBNO:[0-9]+]] [label="#11" color="#FF0000"] +#VC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N1NO]] [tooltip="F0 -> #1" label="" color="black" labelfontcolor="black" penwidth=1.00] +#VC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N2NO]] [tooltip="F0 -> #2" label="" color="black" labelfontcolor="black" penwidth=1.00] +#VC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N3NO]] [tooltip="F0 -> #3" label="" color="black" labelfontcolor="black" penwidth=1.00] +#VC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N4NO]] [tooltip="F0 -> #4" label="" color="black" labelfontcolor="black" penwidth=1.00] +#VC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N5NO]] [tooltip="F0 -> #5" label="" color="black" labelfontcolor="black" penwidth=1.00] +#VC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N6NO]] [tooltip="F0 -> #6" label="" color="black" labelfontcolor="black" penwidth=1.00] +#VC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N7NO]] [tooltip="F0 -> #7" label="" color="black" labelfontcolor="black" penwidth=1.00] +#VC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N8NO]] [tooltip="F0 -> #8" label="" color="black" labelfontcolor="black" penwidth=1.00] +#VC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N9NO]] [tooltip="F0 -> #9" label="" color="black" labelfontcolor="black" penwidth=1.00] +#VC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[NANO]] [tooltip="F0 -> #10" label="" color="#00FF00" labelfontcolor="#00FF00" penwidth=1.00] +#VC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[NBNO]] [tooltip="F0 -> #11" label="" color="#FF0000" labelfontcolor="#FF0000" penwidth=1.00] +#VC-NEXT:} + +#ELVL: digraph xrayDiff { +#ELVL-NEXT: node [shape=record] +#ELVL-DAG: F[[F0NO:[0-9]+]] [label="F0"] +#ELVL-DAG: F[[N1NO:[0-9]+]] [label="{#1|800.00%}" color="black"] +#ELVL-DAG: F[[N2NO:[0-9]+]] [label="{#2|300.00%}" color="black"] +#ELVL-DAG: F[[N3NO:[0-9]+]] [label="{#3|133.33%}" color="black"] +#ELVL-DAG: F[[N4NO:[0-9]+]] [label="{#4|50.00%}" color="black"] +#ELVL-DAG: F[[N5NO:[0-9]+]] [label="{#5|0.00%}" color="black"] +#ELVL-DAG: F[[N6NO:[0-9]+]] [label="{#6|-33.33%}" color="black"] +#ELVL-DAG: F[[N7NO:[0-9]+]] [label="{#7|-57.14%}" color="black"] +#ELVL-DAG: F[[N8NO:[0-9]+]] [label="{#8|-75.00%}" color="black"] +#ELVL-DAG: F[[N9NO:[0-9]+]] [label="{#9|-88.89%}" color="black"] +#ELVL-DAG: F[[NANO:[0-9]+]] [label="#10" color="#00FF00"] +#ELVL-DAG: F[[NBNO:[0-9]+]] [label="#11" color="#FF0000"] +#ELVL-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N1NO]] [tooltip="F0 -> #1" label="800.00%" color="black" labelfontcolor="black" penwidth=1.00] +#ELVL-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N2NO]] [tooltip="F0 -> #2" label="300.00%" color="black" labelfontcolor="black" penwidth=1.00] +#ELVL-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N3NO]] [tooltip="F0 -> #3" label="133.33%" color="black" labelfontcolor="black" penwidth=1.00] +#ELVL-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N4NO]] [tooltip="F0 -> #4" label="50.00%" color="black" labelfontcolor="black" penwidth=1.00] +#ELVL-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N5NO]] [tooltip="F0 -> #5" label="0.00%" color="black" labelfontcolor="black" penwidth=1.00] +#ELVL-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N6NO]] [tooltip="F0 -> #6" label="-33.33%" color="black" labelfontcolor="black" penwidth=1.00] +#ELVL-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N7NO]] [tooltip="F0 -> #7" label="-57.14%" color="black" labelfontcolor="black" penwidth=1.00] +#ELVL-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N8NO]] [tooltip="F0 -> #8" label="-75.00%" color="black" labelfontcolor="black" penwidth=1.00] +#ELVL-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N9NO]] [tooltip="F0 -> #9" label="-88.89%" color="black" labelfontcolor="black" penwidth=1.00] +#ELVL-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[NANO]] [tooltip="F0 -> #10" label="" color="#00FF00" labelfontcolor="#00FF00" penwidth=1.00] +#ELVL-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[NBNO]] [tooltip="F0 -> #11" label="" color="#FF0000" labelfontcolor="#FF0000" penwidth=1.00] +#ELVL-NEXT:} + +#EL: digraph xrayDiff { +#EL-DAG: F[[F0NO:[0-9]+]] [label="F0"] +#EL-DAG: F[[N1NO:[0-9]+]] [label="#1" color="black"] +#EL-DAG: F[[N2NO:[0-9]+]] [label="#2" color="black"] +#EL-DAG: F[[N3NO:[0-9]+]] [label="#3" color="black"] +#EL-DAG: F[[N4NO:[0-9]+]] [label="#4" color="black"] +#EL-DAG: F[[N5NO:[0-9]+]] [label="#5" color="black"] +#EL-DAG: F[[N6NO:[0-9]+]] [label="#6" color="black"] +#EL-DAG: F[[N7NO:[0-9]+]] [label="#7" color="black"] +#EL-DAG: F[[N8NO:[0-9]+]] [label="#8" color="black"] +#EL-DAG: F[[N9NO:[0-9]+]] [label="#9" color="black"] +#EL-DAG: F[[NANO:[0-9]+]] [label="#10" color="#00FF00"] +#EL-DAG: F[[NBNO:[0-9]+]] [label="#11" color="#FF0000"] +#EL-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N1NO]] [tooltip="F0 -> #1" label="800.00%" color="black" labelfontcolor="black" penwidth=1.00] +#EL-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N2NO]] [tooltip="F0 -> #2" label="300.00%" color="black" labelfontcolor="black" penwidth=1.00] +#EL-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N3NO]] [tooltip="F0 -> #3" label="133.33%" color="black" labelfontcolor="black" penwidth=1.00] +#EL-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N4NO]] [tooltip="F0 -> #4" label="50.00%" color="black" labelfontcolor="black" penwidth=1.00] +#EL-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N5NO]] [tooltip="F0 -> #5" label="0.00%" color="black" labelfontcolor="black" penwidth=1.00] +#EL-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N6NO]] [tooltip="F0 -> #6" label="-33.33%" color="black" labelfontcolor="black" penwidth=1.00] +#EL-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N7NO]] [tooltip="F0 -> #7" label="-57.14%" color="black" labelfontcolor="black" penwidth=1.00] +#EL-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N8NO]] [tooltip="F0 -> #8" label="-75.00%" color="black" labelfontcolor="black" penwidth=1.00] +#EL-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N9NO]] [tooltip="F0 -> #9" label="-88.89%" color="black" labelfontcolor="black" penwidth=1.00] +#EL-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[NANO]] [tooltip="F0 -> #10" label="" color="#00FF00" labelfontcolor="#00FF00" penwidth=1.00] +#EL-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[NBNO]] [tooltip="F0 -> #11" label="" color="#FF0000" labelfontcolor="#FF0000" penwidth=1.00] +#EL-NEXT:} + +#VL: digraph xrayDiff { +#VL-NEXT: node [shape=record] +#VL-DAG: F[[F0NO:[0-9]+]] [label="F0"] +#VL-DAG: F[[N1NO:[0-9]+]] [label="{#1|800.00%}" color="black"] +#VL-DAG: F[[N2NO:[0-9]+]] [label="{#2|300.00%}" color="black"] +#VL-DAG: F[[N3NO:[0-9]+]] [label="{#3|133.33%}" color="black"] +#VL-DAG: F[[N4NO:[0-9]+]] [label="{#4|50.00%}" color="black"] +#VL-DAG: F[[N5NO:[0-9]+]] [label="{#5|0.00%}" color="black"] +#VL-DAG: F[[N6NO:[0-9]+]] [label="{#6|-33.33%}" color="black"] +#VL-DAG: F[[N7NO:[0-9]+]] [label="{#7|-57.14%}" color="black"] +#VL-DAG: F[[N8NO:[0-9]+]] [label="{#8|-75.00%}" color="black"] +#VL-DAG: F[[N9NO:[0-9]+]] [label="{#9|-88.89%}" color="black"] +#VL-DAG: F[[NANO:[0-9]+]] [label="#10" color="#00FF00"] +#VL-DAG: F[[NBNO:[0-9]+]] [label="#11" color="#FF0000"] +#VL-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N1NO]] [tooltip="F0 -> #1" label="" color="black" labelfontcolor="black" penwidth=1.00] +#VL-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N2NO]] [tooltip="F0 -> #2" label="" color="black" labelfontcolor="black" penwidth=1.00] +#VL-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N3NO]] [tooltip="F0 -> #3" label="" color="black" labelfontcolor="black" penwidth=1.00] +#VL-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N4NO]] [tooltip="F0 -> #4" label="" color="black" labelfontcolor="black" penwidth=1.00] +#VL-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N5NO]] [tooltip="F0 -> #5" label="" color="black" labelfontcolor="black" penwidth=1.00] +#VL-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N6NO]] [tooltip="F0 -> #6" label="" color="black" labelfontcolor="black" penwidth=1.00] +#VL-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N7NO]] [tooltip="F0 -> #7" label="" color="black" labelfontcolor="black" penwidth=1.00] +#VL-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N8NO]] [tooltip="F0 -> #8" label="" color="black" labelfontcolor="black" penwidth=1.00] +#VL-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N9NO]] [tooltip="F0 -> #9" label="" color="black" labelfontcolor="black" penwidth=1.00] +#VL-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[NANO]] [tooltip="F0 -> #10" label="" color="#00FF00" labelfontcolor="#00FF00" penwidth=1.00] +#VL-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[NBNO]] [tooltip="F0 -> #11" label="" color="#FF0000" labelfontcolor="#FF0000" penwidth=1.00] +#VL-NEXT:} + +#VLVC: digraph xrayDiff { +#VLVC-NEXT: node [shape=record] +#VLVC-DAG: F[[F0NO:[0-9]+]] [label="F0"] +#VLVC-DAG: F[[N1NO:[0-9]+]] [label="{#1|800.00%}" color="#276419"] +#VLVC-DAG: F[[N2NO:[0-9]+]] [label="{#2|300.00%}" color="#276419"] +#VLVC-DAG: F[[N3NO:[0-9]+]] [label="{#3|133.33%}" color="#276419"] +#VLVC-DAG: F[[N4NO:[0-9]+]] [label="{#4|50.00%}" color="#9BCF61"] +#VLVC-DAG: F[[N5NO:[0-9]+]] [label="{#5|0.00%}" color="#F7F7F7"] +#VLVC-DAG: F[[N6NO:[0-9]+]] [label="{#6|-33.33%}" color="#F5C4E0"] +#VLVC-DAG: F[[N7NO:[0-9]+]] [label="{#7|-57.14%}" color="#E17FB4"] +#VLVC-DAG: F[[N8NO:[0-9]+]] [label="{#8|-75.00%}" color="#CB3088"] +#VLVC-DAG: F[[N9NO:[0-9]+]] [label="{#9|-88.89%}" color="#AD0E69"] +#VLVC-DAG: F[[NANO:[0-9]+]] [label="#10" color="#00FF00"] +#VLVC-DAG: F[[NBNO:[0-9]+]] [label="#11" color="#FF0000"] +#VLVC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N1NO]] [tooltip="F0 -> #1" label="" color="black" labelfontcolor="black" penwidth=1.00] +#VLVC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N2NO]] [tooltip="F0 -> #2" label="" color="black" labelfontcolor="black" penwidth=1.00] +#VLVC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N3NO]] [tooltip="F0 -> #3" label="" color="black" labelfontcolor="black" penwidth=1.00] +#VLVC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N4NO]] [tooltip="F0 -> #4" label="" color="black" labelfontcolor="black" penwidth=1.00] +#VLVC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N5NO]] [tooltip="F0 -> #5" label="" color="black" labelfontcolor="black" penwidth=1.00] +#VLVC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N6NO]] [tooltip="F0 -> #6" label="" color="black" labelfontcolor="black" penwidth=1.00] +#VLVC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N7NO]] [tooltip="F0 -> #7" label="" color="black" labelfontcolor="black" penwidth=1.00] +#VLVC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N8NO]] [tooltip="F0 -> #8" label="" color="black" labelfontcolor="black" penwidth=1.00] +#VLVC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N9NO]] [tooltip="F0 -> #9" label="" color="black" labelfontcolor="black" penwidth=1.00] +#VLVC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[NANO]] [tooltip="F0 -> #10" label="" color="#00FF00" labelfontcolor="#00FF00" penwidth=1.00] +#VLVC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[NBNO]] [tooltip="F0 -> #11" label="" color="#FF0000" labelfontcolor="#FF0000" penwidth=1.00] +#VLVC-NEXT:} + +#ELEC: digraph xrayDiff { +#ELEC-DAG: F[[F0NO:[0-9]+]] [label="F0"] +#ELEC-DAG: F[[N1NO:[0-9]+]] [label="#1" color="black"] +#ELEC-DAG: F[[N2NO:[0-9]+]] [label="#2" color="black"] +#ELEC-DAG: F[[N3NO:[0-9]+]] [label="#3" color="black"] +#ELEC-DAG: F[[N4NO:[0-9]+]] [label="#4" color="black"] +#ELEC-DAG: F[[N5NO:[0-9]+]] [label="#5" color="black"] +#ELEC-DAG: F[[N6NO:[0-9]+]] [label="#6" color="black"] +#ELEC-DAG: F[[N7NO:[0-9]+]] [label="#7" color="black"] +#ELEC-DAG: F[[N8NO:[0-9]+]] [label="#8" color="black"] +#ELEC-DAG: F[[N9NO:[0-9]+]] [label="#9" color="black"] +#ELEC-DAG: F[[NANO:[0-9]+]] [label="#10" color="#00FF00"] +#ELEC-DAG: F[[NBNO:[0-9]+]] [label="#11" color="#FF0000"] +#ELEC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N1NO]] [tooltip="F0 -> #1" label="800.00%" color="#276419" labelfontcolor="#276419" penwidth=8.00] +#ELEC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N2NO]] [tooltip="F0 -> #2" label="300.00%" color="#276419" labelfontcolor="#276419" penwidth=3.00] +#ELEC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N3NO]] [tooltip="F0 -> #3" label="133.33%" color="#276419" labelfontcolor="#276419" penwidth=1.33] +#ELEC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N4NO]] [tooltip="F0 -> #4" label="50.00%" color="#9BCF61" labelfontcolor="#9BCF61" penwidth=1.00] +#ELEC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N5NO]] [tooltip="F0 -> #5" label="0.00%" color="#F7F7F7" labelfontcolor="#F7F7F7" penwidth=1.00] +#ELEC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N6NO]] [tooltip="F0 -> #6" label="-33.33%" color="#F5C4E0" labelfontcolor="#F5C4E0" penwidth=1.00] +#ELEC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N7NO]] [tooltip="F0 -> #7" label="-57.14%" color="#E17FB4" labelfontcolor="#E17FB4" penwidth=1.00] +#ELEC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N8NO]] [tooltip="F0 -> #8" label="-75.00%" color="#CB3088" labelfontcolor="#CB3088" penwidth=1.00] +#ELEC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[N9NO]] [tooltip="F0 -> #9" label="-88.89%" color="#AD0E69" labelfontcolor="#AD0E69" penwidth=1.00] +#ELEC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[NANO]] [tooltip="F0 -> #10" label="" color="#00FF00" labelfontcolor="#00FF00" penwidth=1.00] +#ELEC-DAG: F{{.*}}[[F0NO]] -> F{{.*}}[[NBNO]] [tooltip="F0 -> #11" label="" color="#FF0000" labelfontcolor="#FF0000" penwidth=1.00] +#ELEC-NEXT:} + + Index: llvm/trunk/tools/llvm-xray/CMakeLists.txt =================================================================== --- llvm/trunk/tools/llvm-xray/CMakeLists.txt +++ llvm/trunk/tools/llvm-xray/CMakeLists.txt @@ -14,6 +14,7 @@ xray-extract.cc xray-extract.cc xray-graph.cc + xray-graph-diff.cc xray-registry.cc) add_llvm_tool(llvm-xray llvm-xray.cc ${LLVM_XRAY_TOOLS}) Index: llvm/trunk/tools/llvm-xray/xray-color-helper.h =================================================================== --- llvm/trunk/tools/llvm-xray/xray-color-helper.h +++ llvm/trunk/tools/llvm-xray/xray-color-helper.h @@ -46,6 +46,7 @@ double MaxIn; ArrayRef> ColorMap; + ArrayRef> BoundMap; public: /// Enum of the availible Sequential Color Schemes @@ -73,9 +74,16 @@ std::string getColorString(double Point) const; + // Get the Default color, at the moment allways black. + std::tuple getDefaultColorTuple() const { + return std::make_tuple(0, 0, 0); + } + + std::string getDefaultColorString() const { return "black"; } + // Convert a tuple to a string static std::string getColorString(std::tuple t); }; -} -} +} // namespace xray +} // namespace llvm #endif Index: llvm/trunk/tools/llvm-xray/xray-color-helper.cc =================================================================== --- llvm/trunk/tools/llvm-xray/xray-color-helper.cc +++ llvm/trunk/tools/llvm-xray/xray-color-helper.cc @@ -11,6 +11,7 @@ // //===----------------------------------------------------------------------===// #include +#include #include "xray-color-helper.h" #include "llvm/Support/FormatVariadic.h" @@ -42,8 +43,18 @@ std::make_tuple(5, 112, 176), std::make_tuple(4, 90, 141), std::make_tuple(2, 56, 88)}}; +// Sequential Maps extend the last colors given out of range inputs. +static const std::tuple SequentialBounds[][2] = { + {// The Bounds for the greys color scheme + std::make_tuple(255, 255, 255), std::make_tuple(0, 0, 0)}, + {// The Bounds for the OrRd color Scheme + std::make_tuple(255, 247, 236), std::make_tuple(127, 0, 0)}, + {// The Bounds for the PuBu color Scheme + std::make_tuple(255, 247, 251), std::make_tuple(2, 56, 88)}}; + ColorHelper::ColorHelper(ColorHelper::SequentialScheme S) - : MinIn(0.0), MaxIn(1.0), ColorMap(SequentialMaps[static_cast(S)]) {} + : MinIn(0.0), MaxIn(1.0), ColorMap(SequentialMaps[static_cast(S)]), + BoundMap(SequentialBounds[static_cast(S)]) {} // Diverging ColorMaps, which are used to represent information // representing differenes, or a range that goes from negative to positive. @@ -58,8 +69,16 @@ std::make_tuple(127, 188, 65), std::make_tuple(77, 146, 33), std::make_tuple(39, 100, 25)}}; +// Diverging maps use out of bounds ranges to show missing data. Missing Right +// Being below min, and missing left being above max. +static const std::tuple DivergingBounds[][2] = { + {// The PiYG color scheme has green and red for missing right and left + // respectively. + std::make_tuple(255, 0, 0), std::make_tuple(0, 255, 0)}}; + ColorHelper::ColorHelper(ColorHelper::DivergingScheme S) - : MinIn(-1.0), MaxIn(1.0), ColorMap(DivergingCoeffs[static_cast(S)]) {} + : MinIn(-1.0), MaxIn(1.0), ColorMap(DivergingCoeffs[static_cast(S)]), + BoundMap(DivergingBounds[static_cast(S)]) {} // Takes a tuple of uint8_ts representing a color in RGB and converts them to // HSV represented by a tuple of doubles @@ -78,12 +97,12 @@ double C = Scaled[Max] - Scaled[Min]; - double HPrime = (Scaled[(Max + 1) % 3] - Scaled[(Max + 2) % 3]) / C; + double HPrime = + (C == 0) ? 0 : (Scaled[(Max + 1) % 3] - Scaled[(Max + 2) % 3]) / C; HPrime = HPrime + 2.0 * Max; double H = (HPrime < 0) ? (HPrime + 6.0) * 60 : HPrime * 60; // Scale to between 0 and 360 - double V = Scaled[Max]; double S = (V == 0.0) ? 0.0 : C / V; @@ -164,6 +183,13 @@ std::tuple ColorHelper::getColorTuple(double Point) const { assert(!ColorMap.empty() && "ColorMap must not be empty!"); + assert(!BoundMap.empty() && "BoundMap must not be empty!"); + + if (Point < MinIn) + return BoundMap[0]; + if (Point > MaxIn) + return BoundMap[1]; + size_t MaxIndex = ColorMap.size() - 1; double IntervalWidth = MaxIn - MinIn; double OffsetP = Point - MinIn; Index: llvm/trunk/tools/llvm-xray/xray-graph-diff.h =================================================================== --- llvm/trunk/tools/llvm-xray/xray-graph-diff.h +++ llvm/trunk/tools/llvm-xray/xray-graph-diff.h @@ -0,0 +1,74 @@ +//===-- xray-graph-diff.h - XRay Graph Diff Renderer ------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Generate a DOT file to represent the difference between the function call +// graph of two differnent traces. +// +//===----------------------------------------------------------------------===// + +#ifndef XRAY_GRAPH_DIFF_H +#define XRAY_GRAPH_DIFF_H + +#include "xray-graph.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/XRay/Graph.h" + +namespace llvm { +namespace xray { + +// This class creates a graph representing the difference between two +// xray-graphs And allows you to print it to a dot file, with optional color +// coding. +class GraphDiffRenderer { + static const int N = 2; + +public: + using StatType = GraphRenderer::StatType; + using TimeStat = GraphRenderer::TimeStat; + + using GREdgeValueType = GraphRenderer::GraphT::EdgeValueType; + using GRVertexValueType = GraphRenderer::GraphT::VertexValueType; + + struct EdgeAttribute { + std::array CorrEdgePtr = {}; + }; + + struct VertexAttribute { + std::array CorrVertexPtr = {}; + }; + + using GraphT = Graph; + + class Factory { + std::array, N> G; + + public: + template Factory(Ts &... Args) : G{{Args...}} {}; + + Expected getGraphDiffRenderer(); + }; + +private: + GraphT G; + + GraphDiffRenderer() = default; + +public: + void exportGraphAsDOT(raw_ostream &OS, StatType EdgeLabel = StatType::NONE, + StatType EdgeColor = StatType::NONE, + StatType VertexLabel = StatType::NONE, + StatType VertexColor = StatType::NONE, + int TruncLen = 40); + + const GraphT &getGraph() { return G; }; +}; +} // namespace xray +} // namespace llvm + +#endif Index: llvm/trunk/tools/llvm-xray/xray-graph-diff.cc =================================================================== --- llvm/trunk/tools/llvm-xray/xray-graph-diff.cc +++ llvm/trunk/tools/llvm-xray/xray-graph-diff.cc @@ -0,0 +1,484 @@ +//===-- xray-graph-diff.cc - XRay Function Call Graph Renderer ------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Generate a DOT file to represent the function call graph encountered in +// the trace. +// +//===----------------------------------------------------------------------===// +#include +#include +#include +#include + +#include "xray-graph-diff.h" +#include "xray-graph.h" +#include "xray-registry.h" + +#include "xray-color-helper.h" +#include "llvm/ADT/iterator_range.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/XRay/Trace.h" + +using namespace llvm; +using namespace xray; + +static cl::SubCommand GraphDiff("graph-diff", + "Generate diff of function-call graphs"); +static cl::opt GraphDiffInput1(cl::Positional, + cl::desc(""), + cl::Required, cl::sub(GraphDiff)); +static cl::opt GraphDiffInput2(cl::Positional, + cl::desc(""), + cl::Required, cl::sub(GraphDiff)); + +static cl::opt + GraphDiffKeepGoing("keep-going", + cl::desc("Keep going on errors encountered"), + cl::sub(GraphDiff), cl::init(false)); +static cl::alias GraphDiffKeepGoingA("k", cl::aliasopt(GraphDiffKeepGoing), + cl::desc("Alias for -keep-going"), + cl::sub(GraphDiff)); +static cl::opt + GraphDiffKeepGoing1("keep-going-1", + cl::desc("Keep going on errors encountered in trace 1"), + cl::sub(GraphDiff), cl::init(false)); +static cl::alias GraphDiffKeepGoing1A("k1", cl::aliasopt(GraphDiffKeepGoing1), + cl::desc("Alias for -keep-going-1"), + cl::sub(GraphDiff)); +static cl::opt + GraphDiffKeepGoing2("keep-going-2", + cl::desc("Keep going on errors encountered in trace 2"), + cl::sub(GraphDiff), cl::init(false)); +static cl::alias GraphDiffKeepGoing2A("k2", cl::aliasopt(GraphDiffKeepGoing2), + cl::desc("Alias for -keep-going-2"), + cl::sub(GraphDiff)); + +static cl::opt + GraphDiffInstrMap("instr-map", + cl::desc("binary with the instrumentation map, or " + "a separate instrumentation map for graph"), + cl::value_desc("binary with xray_instr_map or yaml"), + cl::sub(GraphDiff), cl::init("")); +static cl::alias GraphDiffInstrMapA("m", cl::aliasopt(GraphDiffInstrMap), + cl::desc("Alias for -instr-map"), + cl::sub(GraphDiff)); +static cl::opt + GraphDiffInstrMap1("instr-map-1", + cl::desc("binary with the instrumentation map, or " + "a separate instrumentation map for graph 1"), + cl::value_desc("binary with xray_instr_map or yaml"), + cl::sub(GraphDiff), cl::init("")); +static cl::alias GraphDiffInstrMap1A("m1", cl::aliasopt(GraphDiffInstrMap1), + cl::desc("Alias for -instr-map-1"), + cl::sub(GraphDiff)); +static cl::opt + GraphDiffInstrMap2("instr-map-2", + cl::desc("binary with the instrumentation map, or " + "a separate instrumentation map for graph 2"), + cl::value_desc("binary with xray_instr_map or yaml"), + cl::sub(GraphDiff), cl::init("")); +static cl::alias GraphDiffInstrMap2A("m2", cl::aliasopt(GraphDiffInstrMap2), + cl::desc("Alias for -instr-map-2"), + cl::sub(GraphDiff)); + +static cl::opt GraphDiffDeduceSiblingCalls( + "deduce-sibling-calls", + cl::desc("Deduce sibling calls when unrolling function call stacks"), + cl::sub(GraphDiff), cl::init(false)); +static cl::alias + GraphDiffDeduceSiblingCallsA("d", cl::aliasopt(GraphDiffDeduceSiblingCalls), + cl::desc("Alias for -deduce-sibling-calls"), + cl::sub(GraphDiff)); +static cl::opt GraphDiffDeduceSiblingCalls1( + "deduce-sibling-calls-1", + cl::desc("Deduce sibling calls when unrolling function call stacks"), + cl::sub(GraphDiff), cl::init(false)); +static cl::alias GraphDiffDeduceSiblingCalls1A( + "d1", cl::aliasopt(GraphDiffDeduceSiblingCalls1), + cl::desc("Alias for -deduce-sibling-calls-1"), cl::sub(GraphDiff)); +static cl::opt GraphDiffDeduceSiblingCalls2( + "deduce-sibling-calls-2", + cl::desc("Deduce sibling calls when unrolling function call stacks"), + cl::sub(GraphDiff), cl::init(false)); +static cl::alias GraphDiffDeduceSiblingCalls2A( + "d2", cl::aliasopt(GraphDiffDeduceSiblingCalls2), + cl::desc("Alias for -deduce-sibling-calls-2"), cl::sub(GraphDiff)); + +static cl::opt GraphDiffEdgeLabel( + "edge-label", cl::desc("Output graphs with edges labeled with this field"), + cl::value_desc("field"), cl::sub(GraphDiff), + cl::init(GraphRenderer::StatType::NONE), + cl::values(clEnumValN(GraphRenderer::StatType::NONE, "none", + "Do not label Edges"), + clEnumValN(GraphRenderer::StatType::COUNT, "count", + "function call counts"), + clEnumValN(GraphRenderer::StatType::MIN, "min", + "minimum function durations"), + clEnumValN(GraphRenderer::StatType::MED, "med", + "median function durations"), + clEnumValN(GraphRenderer::StatType::PCT90, "90p", + "90th percentile durations"), + clEnumValN(GraphRenderer::StatType::PCT99, "99p", + "99th percentile durations"), + clEnumValN(GraphRenderer::StatType::MAX, "max", + "maximum function durations"), + clEnumValN(GraphRenderer::StatType::SUM, "sum", + "sum of call durations"))); +static cl::alias GraphDiffEdgeLabelA("e", cl::aliasopt(GraphDiffEdgeLabel), + cl::desc("Alias for -edge-label"), + cl::sub(GraphDiff)); + +static cl::opt GraphDiffEdgeColor( + "edge-color", cl::desc("Output graphs with edges colored by this field"), + cl::value_desc("field"), cl::sub(GraphDiff), + cl::init(GraphRenderer::StatType::NONE), + cl::values(clEnumValN(GraphRenderer::StatType::NONE, "none", + "Do not color Edges"), + clEnumValN(GraphRenderer::StatType::COUNT, "count", + "function call counts"), + clEnumValN(GraphRenderer::StatType::MIN, "min", + "minimum function durations"), + clEnumValN(GraphRenderer::StatType::MED, "med", + "median function durations"), + clEnumValN(GraphRenderer::StatType::PCT90, "90p", + "90th percentile durations"), + clEnumValN(GraphRenderer::StatType::PCT99, "99p", + "99th percentile durations"), + clEnumValN(GraphRenderer::StatType::MAX, "max", + "maximum function durations"), + clEnumValN(GraphRenderer::StatType::SUM, "sum", + "sum of call durations"))); +static cl::alias GraphDiffEdgeColorA("c", cl::aliasopt(GraphDiffEdgeColor), + cl::desc("Alias for -edge-color"), + cl::sub(GraphDiff)); + +static cl::opt GraphDiffVertexLabel( + "vertex-label", + cl::desc("Output graphs with vertices labeled with this field"), + cl::value_desc("field"), cl::sub(GraphDiff), + cl::init(GraphRenderer::StatType::NONE), + cl::values(clEnumValN(GraphRenderer::StatType::NONE, "none", + "Do not label Vertices"), + clEnumValN(GraphRenderer::StatType::COUNT, "count", + "function call counts"), + clEnumValN(GraphRenderer::StatType::MIN, "min", + "minimum function durations"), + clEnumValN(GraphRenderer::StatType::MED, "med", + "median function durations"), + clEnumValN(GraphRenderer::StatType::PCT90, "90p", + "90th percentile durations"), + clEnumValN(GraphRenderer::StatType::PCT99, "99p", + "99th percentile durations"), + clEnumValN(GraphRenderer::StatType::MAX, "max", + "maximum function durations"), + clEnumValN(GraphRenderer::StatType::SUM, "sum", + "sum of call durations"))); +static cl::alias GraphDiffVertexLabelA("v", cl::aliasopt(GraphDiffVertexLabel), + cl::desc("Alias for -vertex-label"), + cl::sub(GraphDiff)); + +static cl::opt GraphDiffVertexColor( + "vertex-color", + cl::desc("Output graphs with vertices colored by this field"), + cl::value_desc("field"), cl::sub(GraphDiff), + cl::init(GraphRenderer::StatType::NONE), + cl::values(clEnumValN(GraphRenderer::StatType::NONE, "none", + "Do not color Vertices"), + clEnumValN(GraphRenderer::StatType::COUNT, "count", + "function call counts"), + clEnumValN(GraphRenderer::StatType::MIN, "min", + "minimum function durations"), + clEnumValN(GraphRenderer::StatType::MED, "med", + "median function durations"), + clEnumValN(GraphRenderer::StatType::PCT90, "90p", + "90th percentile durations"), + clEnumValN(GraphRenderer::StatType::PCT99, "99p", + "99th percentile durations"), + clEnumValN(GraphRenderer::StatType::MAX, "max", + "maximum function durations"), + clEnumValN(GraphRenderer::StatType::SUM, "sum", + "sum of call durations"))); +static cl::alias GraphDiffVertexColorA("b", cl::aliasopt(GraphDiffVertexColor), + cl::desc("Alias for -vertex-color"), + cl::sub(GraphDiff)); + +static cl::opt GraphDiffVertexLabelTrunc( + "vertex-label-trun", cl::desc("What length to truncate vertex labels to "), + cl::sub(GraphDiff), cl::init(40)); +static cl::alias + GraphDiffVertexLabelTrunc1("t", cl::aliasopt(GraphDiffVertexLabelTrunc), + cl::desc("Alias for -vertex-label-trun"), + cl::sub(GraphDiff)); + +static cl::opt + GraphDiffOutput("output", cl::value_desc("Output file"), cl::init("-"), + cl::desc("output file; use '-' for stdout"), + cl::sub(GraphDiff)); +static cl::alias GraphDiffOutputA("o", cl::aliasopt(GraphDiffOutput), + cl::desc("Alias for -output"), + cl::sub(GraphDiff)); + +Expected GraphDiffRenderer::Factory::getGraphDiffRenderer() { + GraphDiffRenderer R; + + for (int i = 0; i < N; ++i) { + const auto &G = this->G[i].get(); + for (const auto &V : G.vertices()) { + const auto &VAttr = V.second; + R.G[VAttr.SymbolName].CorrVertexPtr[i] = &V; + } + for (const auto &E : G.edges()) { + auto &EdgeTailID = E.first.first; + auto &EdgeHeadID = E.first.second; + auto EdgeTailAttrOrErr = G.at(EdgeTailID); + auto EdgeHeadAttrOrErr = G.at(EdgeHeadID); + if (!EdgeTailAttrOrErr) + return EdgeTailAttrOrErr.takeError(); + if (!EdgeHeadAttrOrErr) + return EdgeHeadAttrOrErr.takeError(); + GraphT::EdgeIdentifier ID{EdgeTailAttrOrErr->SymbolName, + EdgeHeadAttrOrErr->SymbolName}; + R.G[ID].CorrEdgePtr[i] = &E; + } + } + + return R; +} +// Returns the Relative change With respect to LeftStat between LeftStat +// and RightStat. +static double statRelDiff(const GraphDiffRenderer::TimeStat &LeftStat, + const GraphDiffRenderer::TimeStat &RightStat, + GraphDiffRenderer::StatType T) { + double LeftAttr = LeftStat.getDouble(T); + double RightAttr = RightStat.getDouble(T); + + return RightAttr / LeftAttr - 1.0; +} + +static std::string getColor(const GraphDiffRenderer::GraphT::EdgeValueType &E, + const GraphDiffRenderer::GraphT &G, ColorHelper H, + GraphDiffRenderer::StatType T) { + auto &EdgeAttr = E.second; + if (EdgeAttr.CorrEdgePtr[0] == nullptr) + return H.getColorString(2.0); // A number greater than 1.0 + if (EdgeAttr.CorrEdgePtr[1] == nullptr) + return H.getColorString(-2.0); // A number less than -1.0 + + if (T == GraphDiffRenderer::StatType::NONE) + return H.getDefaultColorString(); + + const auto &LeftStat = EdgeAttr.CorrEdgePtr[0]->second.S; + const auto &RightStat = EdgeAttr.CorrEdgePtr[1]->second.S; + + double RelDiff = statRelDiff(LeftStat, RightStat, T); + double CappedRelDiff = std::min(1.0, std::max(-1.0, RelDiff)); + + return H.getColorString(CappedRelDiff); +} + +static std::string getColor(const GraphDiffRenderer::GraphT::VertexValueType &V, + const GraphDiffRenderer::GraphT &G, ColorHelper H, + GraphDiffRenderer::StatType T) { + auto &VertexAttr = V.second; + if (VertexAttr.CorrVertexPtr[0] == nullptr) + return H.getColorString(2.0); // A number greater than 1.0 + if (VertexAttr.CorrVertexPtr[1] == nullptr) + return H.getColorString(-2.0); // A number less than -1.0 + + if (T == GraphDiffRenderer::StatType::NONE) + return H.getDefaultColorString(); + + const auto &LeftStat = VertexAttr.CorrVertexPtr[0]->second.S; + const auto &RightStat = VertexAttr.CorrVertexPtr[1]->second.S; + + double RelDiff = statRelDiff(LeftStat, RightStat, T); + double CappedRelDiff = std::min(1.0, std::max(-1.0, RelDiff)); + + return H.getColorString(CappedRelDiff); +} + +static Twine truncateString(const StringRef &S, size_t n) { + return (S.size() > n) ? Twine(S.substr(0, n)) + "..." : Twine(S); +} + +template static bool containsNullptr(const T &Collection) { + for (const auto &E : Collection) + if (E == nullptr) + return true; + return false; +} + +static std::string getLabel(const GraphDiffRenderer::GraphT::EdgeValueType &E, + GraphDiffRenderer::StatType EL) { + auto &EdgeAttr = E.second; + switch (EL) { + case GraphDiffRenderer::StatType::NONE: + return ""; + default: + if (containsNullptr(EdgeAttr.CorrEdgePtr)) + return ""; + + const auto &LeftStat = EdgeAttr.CorrEdgePtr[0]->second.S; + const auto &RightStat = EdgeAttr.CorrEdgePtr[1]->second.S; + + double RelDiff = statRelDiff(LeftStat, RightStat, EL); + return formatv(R"({0:P})", RelDiff); + } +} + +static std::string getLabel(const GraphDiffRenderer::GraphT::VertexValueType &V, + GraphDiffRenderer::StatType VL, int TrunLen) { + const auto &VertexId = V.first; + const auto &VertexAttr = V.second; + switch (VL) { + case GraphDiffRenderer::StatType::NONE: + return formatv(R"({0})", truncateString(VertexId, TrunLen).str()); + default: + if (containsNullptr(VertexAttr.CorrVertexPtr)) + return formatv(R"({0})", truncateString(VertexId, TrunLen).str()); + + const auto &LeftStat = VertexAttr.CorrVertexPtr[0]->second.S; + const auto &RightStat = VertexAttr.CorrVertexPtr[1]->second.S; + + double RelDiff = statRelDiff(LeftStat, RightStat, VL); + return formatv(R"({{{0}|{1:P}})", truncateString(VertexId, TrunLen).str(), + RelDiff); + } +} + +static double getLineWidth(const GraphDiffRenderer::GraphT::EdgeValueType &E, + GraphDiffRenderer::StatType EL) { + auto &EdgeAttr = E.second; + switch (EL) { + case GraphDiffRenderer::StatType::NONE: + return 1.0; + default: + if (containsNullptr(EdgeAttr.CorrEdgePtr)) + return 1.0; + + const auto &LeftStat = EdgeAttr.CorrEdgePtr[0]->second.S; + const auto &RightStat = EdgeAttr.CorrEdgePtr[1]->second.S; + + double RelDiff = statRelDiff(LeftStat, RightStat, EL); + return (RelDiff > 1.0) ? RelDiff : 1.0; + } +} + +void GraphDiffRenderer::exportGraphAsDOT(raw_ostream &OS, StatType EdgeLabel, + StatType EdgeColor, + StatType VertexLabel, + StatType VertexColor, int TruncLen) { + // Get numbering of vertices for dot output. + StringMap VertexNo; + + int i = 0; + for (const auto &V : G.vertices()) { + VertexNo[V.first] = i++; + } + + ColorHelper H(ColorHelper::DivergingScheme::PiYG); + + OS << "digraph xrayDiff {\n"; + + if (VertexLabel != StatType::NONE) + OS << "node [shape=record]\n"; + + for (const auto &E : G.edges()) { + const auto &HeadId = E.first.first; + const auto &TailId = E.first.second; + OS << formatv(R"(F{0} -> F{1} [tooltip="{2} -> {3}" label="{4}" )" + R"(color="{5}" labelfontcolor="{5}" penwidth={6}])" + "\n", + VertexNo[HeadId], VertexNo[TailId], + (HeadId.equals("")) ? static_cast("F0") : HeadId, + TailId, getLabel(E, EdgeLabel), getColor(E, G, H, EdgeColor), + getLineWidth(E, EdgeColor)); + } + + for (const auto &V : G.vertices()) { + const auto &VertexId = V.first; + if (VertexId.equals("")) { + OS << formatv(R"(F{0} [label="F0"])" + "\n", + VertexNo[VertexId]); + continue; + } + OS << formatv(R"(F{0} [label="{1}" color="{2}"])" + "\n", + VertexNo[VertexId], getLabel(V, VertexLabel, TruncLen), + getColor(V, G, H, VertexColor)); + } + + OS << "}\n"; +}; + +template static T &ifSpecified(T &A, cl::alias &AA, T &B) { + if (A.getPosition() == 0 && AA.getPosition() == 0) + return B; + + return A; +} + +static CommandRegistration Unused(&GraphDiff, []() -> Error { + std::array Factories{ + {{ifSpecified(GraphDiffKeepGoing1, GraphDiffKeepGoing1A, + GraphDiffKeepGoing), + ifSpecified(GraphDiffDeduceSiblingCalls1, GraphDiffDeduceSiblingCalls1A, + GraphDiffDeduceSiblingCalls), + ifSpecified(GraphDiffInstrMap1, GraphDiffInstrMap1A, GraphDiffInstrMap), + Trace()}, + {ifSpecified(GraphDiffKeepGoing2, GraphDiffKeepGoing2A, + GraphDiffKeepGoing), + ifSpecified(GraphDiffDeduceSiblingCalls2, GraphDiffDeduceSiblingCalls2A, + GraphDiffDeduceSiblingCalls), + ifSpecified(GraphDiffInstrMap2, GraphDiffInstrMap2A, GraphDiffInstrMap), + Trace()}}}; + + std::array Inputs{{GraphDiffInput1, GraphDiffInput2}}; + + std::array Graphs; + + for (int i = 0; i < 2; i++) { + auto TraceOrErr = loadTraceFile(Inputs[i], true); + if (!TraceOrErr) + return make_error( + Twine("Failed Loading Input File '") + Inputs[i] + "'", + make_error_code(llvm::errc::invalid_argument)); + Factories[i].Trace = std::move(*TraceOrErr); + + auto GraphRendererOrErr = Factories[i].getGraphRenderer(); + + if (!GraphRendererOrErr) + return GraphRendererOrErr.takeError(); + + auto GraphRenderer = *GraphRendererOrErr; + + Graphs[i] = GraphRenderer.getGraph(); + } + + GraphDiffRenderer::Factory DGF(Graphs[0], Graphs[1]); + + auto GDROrErr = DGF.getGraphDiffRenderer(); + if (!GDROrErr) + return GDROrErr.takeError(); + + auto &GDR = *GDROrErr; + + std::error_code EC; + raw_fd_ostream OS(GraphDiffOutput, EC, sys::fs::OpenFlags::F_Text); + if (EC) + return make_error( + Twine("Cannot open file '") + GraphDiffOutput + "' for writing.", EC); + + GDR.exportGraphAsDOT(OS, GraphDiffEdgeLabel, GraphDiffEdgeColor, + GraphDiffVertexLabel, GraphDiffVertexColor, + GraphDiffVertexLabelTrunc); + + return Error::success(); +}); Index: llvm/trunk/tools/llvm-xray/xray-graph.h =================================================================== --- llvm/trunk/tools/llvm-xray/xray-graph.h +++ llvm/trunk/tools/llvm-xray/xray-graph.h @@ -41,17 +41,18 @@ /// An inner struct for common timing statistics information struct TimeStat { - uint64_t Count = 0; - double Min = 0; - double Median = 0; - double Pct90 = 0; - double Pct99 = 0; - double Max = 0; - double Sum = 0; - std::string getAsString(StatType T) const; - double compare(StatType T, const TimeStat &Other) const; + int64_t Count; + double Min; + double Median; + double Pct90; + double Pct99; + double Max; + double Sum; + + std::string getString(StatType T) const; + double getDouble(StatType T) const; }; - typedef uint64_t TimestampT; + using TimestampT = uint64_t; /// An inner struct for storing edge attributes for our graph. Here the /// attributes are mainly function call statistics. @@ -68,7 +69,7 @@ /// FIXME: Store more attributes based on instrumentation map. struct FunctionStats { std::string SymbolName; - TimeStat S; + TimeStat S = {}; }; struct FunctionAttr { @@ -76,10 +77,10 @@ uint64_t TSC; }; - typedef SmallVector FunctionStack; + using FunctionStack = SmallVector; - typedef DenseMap - PerThreadFunctionStackMap; + using PerThreadFunctionStackMap = + DenseMap; class GraphT : public Graph { public: @@ -88,8 +89,8 @@ }; GraphT G; - typedef typename decltype(G)::VertexIdentifier VertexIdentifier; - typedef typename decltype(G)::EdgeIdentifier EdgeIdentifier; + using VertexIdentifier = typename decltype(G)::VertexIdentifier; + using EdgeIdentifier = decltype(G)::EdgeIdentifier; /// Use a Map to store the Function stack for each thread whilst building the /// graph. @@ -98,7 +99,7 @@ PerThreadFunctionStackMap PerThreadFunctionStack; /// Usefull object for getting human readable Symbol Names. - const FuncIdConversionHelper &FuncIdHelper; + FuncIdConversionHelper FuncIdHelper; bool DeduceSiblingCalls = false; TimestampT CurrentMaxTSC = 0; @@ -143,22 +144,90 @@ return PerThreadFunctionStack; } + class Factory { + public: + bool KeepGoing; + bool DeduceSiblingCalls; + std::string InstrMap; + Trace Trace; + Expected getGraphRenderer(); + }; + /// Output the Embedded graph in DOT format on \p OS, labeling the edges by /// \p T - void exportGraphAsDOT(raw_ostream &OS, const XRayFileHeader &H, - StatType EdgeLabel = StatType::NONE, + void exportGraphAsDOT(raw_ostream &OS, StatType EdgeLabel = StatType::NONE, StatType EdgeColor = StatType::NONE, StatType VertexLabel = StatType::NONE, StatType VertexColor = StatType::NONE); /// Get a reference to the internal graph. - const GraphT &getGraph() { - calculateEdgeStatistics(); - calculateVertexStatistics(); - return G; - } + const GraphT &getGraph() { return G; } }; + +/// Vector Sum of TimeStats +inline GraphRenderer::TimeStat operator+(const GraphRenderer::TimeStat &A, + const GraphRenderer::TimeStat &B) { + return {A.Count + B.Count, A.Min + B.Min, A.Median + B.Median, + A.Pct90 + B.Pct90, A.Pct99 + B.Pct99, A.Max + B.Max, + A.Sum + B.Sum}; +} + +/// Vector Difference of Timestats +inline GraphRenderer::TimeStat operator-(const GraphRenderer::TimeStat &A, + const GraphRenderer::TimeStat &B) { + + return {A.Count - B.Count, A.Min - B.Min, A.Median - B.Median, + A.Pct90 - B.Pct90, A.Pct99 - B.Pct99, A.Max - B.Max, + A.Sum - B.Sum}; } + +/// Scalar Diference of TimeStat and double +inline GraphRenderer::TimeStat operator/(const GraphRenderer::TimeStat &A, + double B) { + + return {static_cast(A.Count / B), + A.Min / B, + A.Median / B, + A.Pct90 / B, + A.Pct99 / B, + A.Max / B, + A.Sum / B}; +} + +/// Scalar product of TimeStat and Double +inline GraphRenderer::TimeStat operator*(const GraphRenderer::TimeStat &A, + double B) { + return {static_cast(A.Count * B), + A.Min * B, + A.Median * B, + A.Pct90 * B, + A.Pct99 * B, + A.Max * B, + A.Sum * B}; +} + +/// Scalar product of double TimeStat +inline GraphRenderer::TimeStat operator*(double A, + const GraphRenderer::TimeStat &B) { + return B * A; +} + +/// Hadamard Product of TimeStats +inline GraphRenderer::TimeStat operator*(const GraphRenderer::TimeStat &A, + const GraphRenderer::TimeStat &B) { + return {A.Count * B.Count, A.Min * B.Min, A.Median * B.Median, + A.Pct90 * B.Pct90, A.Pct99 * B.Pct99, A.Max * B.Max, + A.Sum * B.Sum}; +} + +/// Hadamard Division of TimeStats +inline GraphRenderer::TimeStat operator/(const GraphRenderer::TimeStat &A, + const GraphRenderer::TimeStat &B) { + return {A.Count * B.Count, A.Min * B.Min, A.Median * B.Median, + A.Pct90 * B.Pct90, A.Pct99 * B.Pct99, A.Max * B.Max, + A.Sum * B.Sum}; } +} // namespace xray +} // namespace llvm #endif // XRAY_GRAPH_H Index: llvm/trunk/tools/llvm-xray/xray-graph.cc =================================================================== --- llvm/trunk/tools/llvm-xray/xray-graph.cc +++ llvm/trunk/tools/llvm-xray/xray-graph.cc @@ -19,7 +19,6 @@ #include "xray-graph.h" #include "xray-registry.h" -#include "llvm/ADT/ArrayRef.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/XRay/InstrumentationMap.h" @@ -98,7 +97,7 @@ cl::value_desc("field"), cl::sub(GraphC), cl::init(GraphRenderer::StatType::NONE), cl::values(clEnumValN(GraphRenderer::StatType::NONE, "none", - "Do not label Edges"), + "Do not label Vertices"), clEnumValN(GraphRenderer::StatType::COUNT, "count", "function call counts"), clEnumValN(GraphRenderer::StatType::MIN, "min", @@ -123,7 +122,7 @@ cl::value_desc("field"), cl::sub(GraphC), cl::init(GraphRenderer::StatType::NONE), cl::values(clEnumValN(GraphRenderer::StatType::NONE, "none", - "Do not label Edges"), + "Do not color Edges"), clEnumValN(GraphRenderer::StatType::COUNT, "count", "function call counts"), clEnumValN(GraphRenderer::StatType::MIN, "min", @@ -148,7 +147,7 @@ cl::value_desc("field"), cl::sub(GraphC), cl::init(GraphRenderer::StatType::NONE), cl::values(clEnumValN(GraphRenderer::StatType::NONE, "none", - "Do not label Edges"), + "Do not color vertices"), clEnumValN(GraphRenderer::StatType::COUNT, "count", "function call counts"), clEnumValN(GraphRenderer::StatType::MIN, "min", @@ -210,7 +209,7 @@ auto &ThreadStack = PerThreadFunctionStack[Record.TId]; switch (Record.Type) { case RecordTypes::ENTER: { - if (G.count(Record.FuncId) == 0) + if (Record.FuncId != 0 && G.count(Record.FuncId) == 0) G[Record.FuncId].SymbolName = FuncIdHelper.SymbolOrNumber(Record.FuncId); ThreadStack.push_back({Record.FuncId, Record.TSC}); break; @@ -312,12 +311,9 @@ // TimeStat element. static void normalizeTimeStat(GraphRenderer::TimeStat &S, double CycleFrequency) { - S.Min /= CycleFrequency; - S.Median /= CycleFrequency; - S.Max /= CycleFrequency; - S.Sum /= CycleFrequency; - S.Pct90 /= CycleFrequency; - S.Pct99 /= CycleFrequency; + int64_t OldCount = S.Count; + S = S / CycleFrequency; + S.Count = OldCount; } // Normalises the statistics in the graph for a given TSC frequency. @@ -337,32 +333,22 @@ // Returns a string containing the value of statistic field T std::string -GraphRenderer::TimeStat::getAsString(GraphRenderer::StatType T) const { +GraphRenderer::TimeStat::getString(GraphRenderer::StatType T) const { std::string St; raw_string_ostream S{St}; + double TimeStat::*DoubleStatPtrs[] = {&TimeStat::Min, &TimeStat::Median, + &TimeStat::Pct90, &TimeStat::Pct99, + &TimeStat::Max, &TimeStat::Sum}; switch (T) { + case GraphRenderer::StatType::NONE: + break; case GraphRenderer::StatType::COUNT: S << Count; break; - case GraphRenderer::StatType::MIN: - S << Min; - break; - case GraphRenderer::StatType::MED: - S << Median; - break; - case GraphRenderer::StatType::PCT90: - S << Pct90; - break; - case GraphRenderer::StatType::PCT99: - S << Pct99; - break; - case GraphRenderer::StatType::MAX: - S << Max; - break; - case GraphRenderer::StatType::SUM: - S << Sum; - break; - case GraphRenderer::StatType::NONE: + default: + S << (*this).* + DoubleStatPtrs[static_cast(T) - + static_cast(GraphRenderer::StatType::MIN)]; break; } return S.str(); @@ -370,38 +356,25 @@ // Returns the quotient between the property T of this and another TimeStat as // a double -double GraphRenderer::TimeStat::compare(StatType T, const TimeStat &O) const { +double GraphRenderer::TimeStat::getDouble(StatType T) const { double retval = 0; + double TimeStat::*DoubleStatPtrs[] = {&TimeStat::Min, &TimeStat::Median, + &TimeStat::Pct90, &TimeStat::Pct99, + &TimeStat::Max, &TimeStat::Sum}; switch (T) { - case GraphRenderer::StatType::COUNT: - retval = static_cast(Count) / static_cast(O.Count); - break; - case GraphRenderer::StatType::MIN: - retval = Min / O.Min; - break; - case GraphRenderer::StatType::MED: - retval = Median / O.Median; - break; - case GraphRenderer::StatType::PCT90: - retval = Pct90 / O.Pct90; - break; - case GraphRenderer::StatType::PCT99: - retval = Pct99 / O.Pct99; - break; - case GraphRenderer::StatType::MAX: - retval = Max / O.Max; - break; - case GraphRenderer::StatType::SUM: - retval = Sum / O.Sum; - break; case GraphRenderer::StatType::NONE: retval = 0.0; break; + case GraphRenderer::StatType::COUNT: + retval = static_cast(Count); + break; + default: + retval = + (*this).*DoubleStatPtrs[static_cast(T) - + static_cast(GraphRenderer::StatType::MIN)]; + break; } - return std::sqrt( - retval); // the square root here provides more dynamic contrast for - // low runtime edges, giving better separation and - // coloring lower down the call stack. + return retval; } // Outputs a DOT format version of the Graph embedded in the GraphRenderer @@ -410,17 +383,8 @@ // annotations. // // FIXME: output more information, better presented. -void GraphRenderer::exportGraphAsDOT(raw_ostream &OS, const XRayFileHeader &H, - StatType ET, StatType EC, StatType VT, - StatType VC) { - G.GraphEdgeMax = {}; - G.GraphVertexMax = {}; - calculateEdgeStatistics(); - calculateVertexStatistics(); - - if (H.CycleFrequency) - normalizeStatistics(H.CycleFrequency); - +void GraphRenderer::exportGraphAsDOT(raw_ostream &OS, StatType ET, StatType EC, + StatType VT, StatType VC) { OS << "digraph xray {\n"; if (VT != StatType::NONE) @@ -429,9 +393,11 @@ for (const auto &E : G.edges()) { const auto &S = E.second.S; OS << "F" << E.first.first << " -> " - << "F" << E.first.second << " [label=\"" << S.getAsString(ET) << "\""; + << "F" << E.first.second << " [label=\"" << S.getString(ET) << "\""; if (EC != StatType::NONE) - OS << " color=\"" << CHelper.getColorString(S.compare(EC, G.GraphEdgeMax)) + OS << " color=\"" + << CHelper.getColorString( + std::sqrt(S.getDouble(EC) / G.GraphEdgeMax.getDouble(EC))) << "\""; OS << "];\n"; } @@ -444,26 +410,20 @@ << (VA.SymbolName.size() > 40 ? VA.SymbolName.substr(0, 40) + "..." : VA.SymbolName); if (VT != StatType::NONE) - OS << "|" << VA.S.getAsString(VT) << "}\""; + OS << "|" << VA.S.getString(VT) << "}\""; else OS << "\""; if (VC != StatType::NONE) - OS << " color=\"" << CHelper.getColorString(VA.S.compare(VC, G.GraphVertexMax)) + OS << " color=\"" + << CHelper.getColorString( + std::sqrt(VA.S.getDouble(VC) / G.GraphVertexMax.getDouble(VC))) << "\""; OS << "];\n"; } OS << "}\n"; } -// Here we register and implement the llvm-xray graph subcommand. -// The bulk of this code reads in the options, opens the required files, uses -// those files to create a context for analysing the xray trace, then there is a -// short loop which actually analyses the trace, generates the graph and then -// outputs it as a DOT. -// -// FIXME: include additional filtering and annalysis passes to provide more -// specific useful information. -static CommandRegistration Unused(&GraphC, []() -> Error { +Expected GraphRenderer::Factory::getGraphRenderer() { InstrumentationMap Map; if (!GraphInstrMap.empty()) { auto InstrumentationMapOrError = loadInstrumentationMap(GraphInstrMap); @@ -477,30 +437,16 @@ } const auto &FunctionAddresses = Map.getFunctionAddresses(); + symbolize::LLVMSymbolizer::Options Opts( symbolize::FunctionNameKind::LinkageName, true, true, false, ""); symbolize::LLVMSymbolizer Symbolizer(Opts); - llvm::xray::FuncIdConversionHelper FuncIdHelper(GraphInstrMap, Symbolizer, - FunctionAddresses); - xray::GraphRenderer GR(FuncIdHelper, GraphDeduceSiblingCalls); - std::error_code EC; - raw_fd_ostream OS(GraphOutput, EC, sys::fs::OpenFlags::F_Text); - if (EC) - return make_error( - Twine("Cannot open file '") + GraphOutput + "' for writing.", EC); - - auto TraceOrErr = loadTraceFile(GraphInput, true); - if (!TraceOrErr) - return joinErrors( - make_error(Twine("Failed loading input file '") + - GraphInput + "'", - make_error_code(llvm::errc::invalid_argument)), - TraceOrErr.takeError()); - - auto &Trace = *TraceOrErr; const auto &Header = Trace.getFileHeader(); - // Here we generate the call graph from entries we find in the trace. + llvm::xray::FuncIdConversionHelper FuncIdHelper(InstrMap, Symbolizer, + FunctionAddresses); + + xray::GraphRenderer GR(FuncIdHelper, DeduceSiblingCalls); for (const auto &Record : Trace) { auto E = GR.accountRecord(Record); if (!E) @@ -523,7 +469,53 @@ handleAllErrors(std::move(E), [&](const ErrorInfoBase &E) { E.log(errs()); }); } - GR.exportGraphAsDOT(OS, Header, GraphEdgeLabel, GraphEdgeColorType, - GraphVertexLabel, GraphVertexColorType); + + GR.G.GraphEdgeMax = {}; + GR.G.GraphVertexMax = {}; + GR.calculateEdgeStatistics(); + GR.calculateVertexStatistics(); + + if (Header.CycleFrequency) + GR.normalizeStatistics(Header.CycleFrequency); + + return GR; +} + +// Here we register and implement the llvm-xray graph subcommand. +// The bulk of this code reads in the options, opens the required files, uses +// those files to create a context for analysing the xray trace, then there is a +// short loop which actually analyses the trace, generates the graph and then +// outputs it as a DOT. +// +// FIXME: include additional filtering and annalysis passes to provide more +// specific useful information. +static CommandRegistration Unused(&GraphC, []() -> Error { + GraphRenderer::Factory F; + + F.KeepGoing = GraphKeepGoing; + F.DeduceSiblingCalls = GraphDeduceSiblingCalls; + F.InstrMap = GraphInstrMap; + + auto TraceOrErr = loadTraceFile(GraphInput, true); + + if (!TraceOrErr) + return make_error( + Twine("Failed loading input file '") + GraphInput + "'", + make_error_code(llvm::errc::invalid_argument)); + + F.Trace = std::move(*TraceOrErr); + auto GROrError = F.getGraphRenderer(); + if (!GROrError) + return GROrError.takeError(); + auto &GR = *GROrError; + + std::error_code EC; + raw_fd_ostream OS(GraphOutput, EC, sys::fs::OpenFlags::F_Text); + if (EC) + return make_error( + Twine("Cannot open file '") + GraphOutput + "' for writing.", EC); + + GR.exportGraphAsDOT(OS, GraphEdgeLabel, GraphEdgeColorType, GraphVertexLabel, + GraphVertexColorType); return Error::success(); });