[llvm] r301160 - [XRay] A tool for Comparing xray function call graphs

Dean Michael Berris via llvm-commits llvm-commits at lists.llvm.org
Sun Apr 23 22:54:34 PDT 2017


Author: dberris
Date: Mon Apr 24 00:54:33 2017
New Revision: 301160

URL: http://llvm.org/viewvc/llvm-project?rev=301160&view=rev
Log:
[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

Added:
    llvm/trunk/test/tools/llvm-xray/X86/Inputs/graph-diff-A.yaml
    llvm/trunk/test/tools/llvm-xray/X86/Inputs/graph-diff-B.yaml
    llvm/trunk/test/tools/llvm-xray/X86/graph-diff-simple.txt
    llvm/trunk/tools/llvm-xray/xray-graph-diff.cc
    llvm/trunk/tools/llvm-xray/xray-graph-diff.h
Modified:
    llvm/trunk/test/tools/llvm-xray/X86/Inputs/simple-instrmap.yaml
    llvm/trunk/tools/llvm-xray/CMakeLists.txt
    llvm/trunk/tools/llvm-xray/xray-color-helper.cc
    llvm/trunk/tools/llvm-xray/xray-color-helper.h
    llvm/trunk/tools/llvm-xray/xray-graph.cc
    llvm/trunk/tools/llvm-xray/xray-graph.h

Added: llvm/trunk/test/tools/llvm-xray/X86/Inputs/graph-diff-A.yaml
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-xray/X86/Inputs/graph-diff-A.yaml?rev=301160&view=auto
==============================================================================
--- llvm/trunk/test/tools/llvm-xray/X86/Inputs/graph-diff-A.yaml (added)
+++ llvm/trunk/test/tools/llvm-xray/X86/Inputs/graph-diff-A.yaml Mon Apr 24 00:54:33 2017
@@ -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 } 
+---

Added: llvm/trunk/test/tools/llvm-xray/X86/Inputs/graph-diff-B.yaml
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-xray/X86/Inputs/graph-diff-B.yaml?rev=301160&view=auto
==============================================================================
--- llvm/trunk/test/tools/llvm-xray/X86/Inputs/graph-diff-B.yaml (added)
+++ llvm/trunk/test/tools/llvm-xray/X86/Inputs/graph-diff-B.yaml Mon Apr 24 00:54:33 2017
@@ -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 }
+---
+

Modified: llvm/trunk/test/tools/llvm-xray/X86/Inputs/simple-instrmap.yaml
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-xray/X86/Inputs/simple-instrmap.yaml?rev=301160&r1=301159&r2=301160&view=diff
==============================================================================
--- llvm/trunk/test/tools/llvm-xray/X86/Inputs/simple-instrmap.yaml (original)
+++ llvm/trunk/test/tools/llvm-xray/X86/Inputs/simple-instrmap.yaml Mon Apr 24 00:54:33 2017
@@ -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}
 ...

Added: llvm/trunk/test/tools/llvm-xray/X86/graph-diff-simple.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-xray/X86/graph-diff-simple.txt?rev=301160&view=auto
==============================================================================
--- llvm/trunk/test/tools/llvm-xray/X86/graph-diff-simple.txt (added)
+++ llvm/trunk/test/tools/llvm-xray/X86/graph-diff-simple.txt Mon Apr 24 00:54:33 2017
@@ -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:}
+
+

Modified: llvm/trunk/tools/llvm-xray/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-xray/CMakeLists.txt?rev=301160&r1=301159&r2=301160&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-xray/CMakeLists.txt (original)
+++ llvm/trunk/tools/llvm-xray/CMakeLists.txt Mon Apr 24 00:54:33 2017
@@ -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})

Modified: llvm/trunk/tools/llvm-xray/xray-color-helper.cc
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-xray/xray-color-helper.cc?rev=301160&r1=301159&r2=301160&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-xray/xray-color-helper.cc (original)
+++ llvm/trunk/tools/llvm-xray/xray-color-helper.cc Mon Apr 24 00:54:33 2017
@@ -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
      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
      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, u
 
   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,
 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;

Modified: llvm/trunk/tools/llvm-xray/xray-color-helper.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-xray/xray-color-helper.h?rev=301160&r1=301159&r2=301160&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-xray/xray-color-helper.h (original)
+++ llvm/trunk/tools/llvm-xray/xray-color-helper.h Mon Apr 24 00:54:33 2017
@@ -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

Added: llvm/trunk/tools/llvm-xray/xray-graph-diff.cc
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-xray/xray-graph-diff.cc?rev=301160&view=auto
==============================================================================
--- llvm/trunk/tools/llvm-xray/xray-graph-diff.cc (added)
+++ llvm/trunk/tools/llvm-xray/xray-graph-diff.cc Mon Apr 24 00:54:33 2017
@@ -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();
+});

Added: llvm/trunk/tools/llvm-xray/xray-graph-diff.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-xray/xray-graph-diff.h?rev=301160&view=auto
==============================================================================
--- llvm/trunk/tools/llvm-xray/xray-graph-diff.h (added)
+++ llvm/trunk/tools/llvm-xray/xray-graph-diff.h Mon Apr 24 00:54:33 2017
@@ -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

Modified: llvm/trunk/tools/llvm-xray/xray-graph.cc
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-xray/xray-graph.cc?rev=301160&r1=301159&r2=301160&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-xray/xray-graph.cc (original)
+++ llvm/trunk/tools/llvm-xray/xray-graph.cc Mon Apr 24 00:54:33 2017
@@ -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>
     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>
     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>
     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
   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::calculateVertexStati
 // 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(
 
 // 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(Gra
 
 // 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(
 // 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
   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
        << (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(&Graph
   }
 
   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(&Graph
     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();
 });

Modified: llvm/trunk/tools/llvm-xray/xray-graph.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-xray/xray-graph.h?rev=301160&r1=301159&r2=301160&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-xray/xray-graph.h (original)
+++ llvm/trunk/tools/llvm-xray/xray-graph.h Mon Apr 24 00:54:33 2017
@@ -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




More information about the llvm-commits mailing list