mirror of
https://github.com/RPCSX/llvm.git
synced 2024-11-27 13:40:30 +00:00
[XRay] A tool for Comparing xray function call graphs
Summary: This is a tool for comparing the function graphs produced by the llvm-xray graph too. It takes the form of a new subcommand of the llvm-xray tool 'graph-diff'. This initial version of the patch is very rough, but it is close to feature complete. Depends on D29363 Reviewers: dblaikie, dberris Reviewed By: dberris Subscribers: mgorny, llvm-commits Differential Revision: https://reviews.llvm.org/D29320 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@301160 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
6a61317c63
commit
ae0fbfb1fb
29
test/tools/llvm-xray/X86/Inputs/graph-diff-A.yaml
Normal file
29
test/tools/llvm-xray/X86/Inputs/graph-diff-A.yaml
Normal file
@ -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 }
|
||||
---
|
30
test/tools/llvm-xray/X86/Inputs/graph-diff-B.yaml
Normal file
30
test/tools/llvm-xray/X86/Inputs/graph-diff-B.yaml
Normal file
@ -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 }
|
||||
---
|
||||
|
@ -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}
|
||||
...
|
||||
|
238
test/tools/llvm-xray/X86/graph-diff-simple.txt
Normal file
238
test/tools/llvm-xray/X86/graph-diff-simple.txt
Normal file
@ -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:}
|
||||
|
||||
|
@ -14,6 +14,7 @@ set(LLVM_XRAY_TOOLS
|
||||
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})
|
||||
|
@ -11,6 +11,7 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
|
||||
#include "xray-color-helper.h"
|
||||
#include "llvm/Support/FormatVariadic.h"
|
||||
@ -42,8 +43,18 @@ static const std::tuple<uint8_t, uint8_t, uint8_t> SequentialMaps[][9] = {
|
||||
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<uint8_t, uint8_t, uint8_t> 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<int>(S)]) {}
|
||||
: MinIn(0.0), MaxIn(1.0), ColorMap(SequentialMaps[static_cast<int>(S)]),
|
||||
BoundMap(SequentialBounds[static_cast<int>(S)]) {}
|
||||
|
||||
// Diverging ColorMaps, which are used to represent information
|
||||
// representing differenes, or a range that goes from negative to positive.
|
||||
@ -58,8 +69,16 @@ static const std::tuple<uint8_t, uint8_t, uint8_t> DivergingCoeffs[][11] = {
|
||||
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<uint8_t, uint8_t, uint8_t> 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<int>(S)]) {}
|
||||
: MinIn(-1.0), MaxIn(1.0), ColorMap(DivergingCoeffs[static_cast<int>(S)]),
|
||||
BoundMap(DivergingBounds[static_cast<int>(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 @@ convertToHSV(const std::tuple<uint8_t, uint8_t, uint8_t> &Color) {
|
||||
|
||||
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 @@ interpolateHSV(const std::tuple<double, double, double> &C0,
|
||||
std::tuple<uint8_t, uint8_t, uint8_t>
|
||||
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;
|
||||
|
@ -46,6 +46,7 @@ class ColorHelper {
|
||||
double MaxIn;
|
||||
|
||||
ArrayRef<std::tuple<uint8_t, uint8_t, uint8_t>> ColorMap;
|
||||
ArrayRef<std::tuple<uint8_t, uint8_t, uint8_t>> BoundMap;
|
||||
|
||||
public:
|
||||
/// Enum of the availible Sequential Color Schemes
|
||||
@ -73,9 +74,16 @@ public:
|
||||
|
||||
std::string getColorString(double Point) const;
|
||||
|
||||
// Get the Default color, at the moment allways black.
|
||||
std::tuple<uint8_t, uint8_t, uint8_t> 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<uint8_t, uint8_t, uint8_t> t);
|
||||
};
|
||||
}
|
||||
}
|
||||
} // namespace xray
|
||||
} // namespace llvm
|
||||
#endif
|
||||
|
484
tools/llvm-xray/xray-graph-diff.cc
Normal file
484
tools/llvm-xray/xray-graph-diff.cc
Normal file
@ -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 <cassert>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
|
||||
#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<std::string> GraphDiffInput1(cl::Positional,
|
||||
cl::desc("<xray log file 1>"),
|
||||
cl::Required, cl::sub(GraphDiff));
|
||||
static cl::opt<std::string> GraphDiffInput2(cl::Positional,
|
||||
cl::desc("<xray log file 2>"),
|
||||
cl::Required, cl::sub(GraphDiff));
|
||||
|
||||
static cl::opt<bool>
|
||||
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<bool>
|
||||
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<bool>
|
||||
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<std::string>
|
||||
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<std::string>
|
||||
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<std::string>
|
||||
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<bool> 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<bool> 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<bool> 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<GraphRenderer::StatType> 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<GraphRenderer::StatType> 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<GraphRenderer::StatType> 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<GraphRenderer::StatType> 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<int> 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<std::string>
|
||||
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> 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 <typename T> 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<int32_t> 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<StringRef>("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 <typename T> 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<GraphRenderer::Factory, 2> 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<std::string, 2> Inputs{{GraphDiffInput1, GraphDiffInput2}};
|
||||
|
||||
std::array<GraphRenderer::GraphT, 2> Graphs;
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
auto TraceOrErr = loadTraceFile(Inputs[i], true);
|
||||
if (!TraceOrErr)
|
||||
return make_error<StringError>(
|
||||
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<StringError>(
|
||||
Twine("Cannot open file '") + GraphDiffOutput + "' for writing.", EC);
|
||||
|
||||
GDR.exportGraphAsDOT(OS, GraphDiffEdgeLabel, GraphDiffEdgeColor,
|
||||
GraphDiffVertexLabel, GraphDiffVertexColor,
|
||||
GraphDiffVertexLabelTrunc);
|
||||
|
||||
return Error::success();
|
||||
});
|
74
tools/llvm-xray/xray-graph-diff.h
Normal file
74
tools/llvm-xray/xray-graph-diff.h
Normal file
@ -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<const GREdgeValueType *, N> CorrEdgePtr = {};
|
||||
};
|
||||
|
||||
struct VertexAttribute {
|
||||
std::array<const GRVertexValueType *, N> CorrVertexPtr = {};
|
||||
};
|
||||
|
||||
using GraphT = Graph<VertexAttribute, EdgeAttribute, StringRef>;
|
||||
|
||||
class Factory {
|
||||
std::array<std::reference_wrapper<const GraphRenderer::GraphT>, N> G;
|
||||
|
||||
public:
|
||||
template <typename... Ts> Factory(Ts &... Args) : G{{Args...}} {};
|
||||
|
||||
Expected<GraphDiffRenderer> 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
|
@ -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 @@ static cl::opt<GraphRenderer::StatType> GraphVertexLabel(
|
||||
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 @@ static cl::opt<GraphRenderer::StatType> GraphEdgeColorType(
|
||||
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 @@ static cl::opt<GraphRenderer::StatType> GraphVertexColorType(
|
||||
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 @@ Error GraphRenderer::accountRecord(const XRayRecord &Record) {
|
||||
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 @@ void GraphRenderer::calculateVertexStatistics() {
|
||||
// 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 @@ void GraphRenderer::normalizeStatistics(double CycleFrequency) {
|
||||
|
||||
// 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<int>(T) -
|
||||
static_cast<int>(GraphRenderer::StatType::MIN)];
|
||||
break;
|
||||
}
|
||||
return S.str();
|
||||
@ -370,38 +356,25 @@ GraphRenderer::TimeStat::getAsString(GraphRenderer::StatType T) const {
|
||||
|
||||
// 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<double>(Count) / static_cast<double>(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<double>(Count);
|
||||
break;
|
||||
default:
|
||||
retval =
|
||||
(*this).*DoubleStatPtrs[static_cast<int>(T) -
|
||||
static_cast<int>(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 @@ double GraphRenderer::TimeStat::compare(StatType T, const TimeStat &O) const {
|
||||
// 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 @@ void GraphRenderer::exportGraphAsDOT(raw_ostream &OS, const XRayFileHeader &H,
|
||||
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 @@ void GraphRenderer::exportGraphAsDOT(raw_ostream &OS, const XRayFileHeader &H,
|
||||
<< (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> GraphRenderer::Factory::getGraphRenderer() {
|
||||
InstrumentationMap Map;
|
||||
if (!GraphInstrMap.empty()) {
|
||||
auto InstrumentationMapOrError = loadInstrumentationMap(GraphInstrMap);
|
||||
@ -477,30 +437,16 @@ static CommandRegistration Unused(&GraphC, []() -> Error {
|
||||
}
|
||||
|
||||
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<StringError>(
|
||||
Twine("Cannot open file '") + GraphOutput + "' for writing.", EC);
|
||||
|
||||
auto TraceOrErr = loadTraceFile(GraphInput, true);
|
||||
if (!TraceOrErr)
|
||||
return joinErrors(
|
||||
make_error<StringError>(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 @@ static CommandRegistration Unused(&GraphC, []() -> Error {
|
||||
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<StringError>(
|
||||
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<StringError>(
|
||||
Twine("Cannot open file '") + GraphOutput + "' for writing.", EC);
|
||||
|
||||
GR.exportGraphAsDOT(OS, GraphEdgeLabel, GraphEdgeColorType, GraphVertexLabel,
|
||||
GraphVertexColorType);
|
||||
return Error::success();
|
||||
});
|
||||
|
@ -41,17 +41,18 @@ public:
|
||||
|
||||
/// 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 @@ public:
|
||||
/// FIXME: Store more attributes based on instrumentation map.
|
||||
struct FunctionStats {
|
||||
std::string SymbolName;
|
||||
TimeStat S;
|
||||
TimeStat S = {};
|
||||
};
|
||||
|
||||
struct FunctionAttr {
|
||||
@ -76,10 +77,10 @@ public:
|
||||
uint64_t TSC;
|
||||
};
|
||||
|
||||
typedef SmallVector<FunctionAttr, 4> FunctionStack;
|
||||
using FunctionStack = SmallVector<FunctionAttr, 4>;
|
||||
|
||||
typedef DenseMap<llvm::sys::ProcessInfo::ProcessId, FunctionStack>
|
||||
PerThreadFunctionStackMap;
|
||||
using PerThreadFunctionStackMap =
|
||||
DenseMap<llvm::sys::ProcessInfo::ProcessId, FunctionStack>;
|
||||
|
||||
class GraphT : public Graph<FunctionStats, CallStats, int32_t> {
|
||||
public:
|
||||
@ -88,8 +89,8 @@ public:
|
||||
};
|
||||
|
||||
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 @@ public:
|
||||
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 @@ public:
|
||||
return PerThreadFunctionStack;
|
||||
}
|
||||
|
||||
class Factory {
|
||||
public:
|
||||
bool KeepGoing;
|
||||
bool DeduceSiblingCalls;
|
||||
std::string InstrMap;
|
||||
Trace Trace;
|
||||
Expected<GraphRenderer> 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<int64_t>(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<int64_t>(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
|
||||
|
Loading…
Reference in New Issue
Block a user