├── .gitignore ├── LICENSE ├── README.md ├── dub.json ├── dub.selections.json ├── example ├── .gitignore ├── Makefile ├── dub.log ├── example.png ├── profdump.log ├── sample │ ├── profdump.dot │ ├── profdump.json │ ├── profdump.svg │ ├── profdump.txt │ ├── simple.dot │ ├── simple.json │ ├── simple.svg │ └── simple.txt └── simple.log ├── profdump-test-unittest └── source ├── app.d └── profdump.d /.gitignore: -------------------------------------------------------------------------------- 1 | .dub 2 | docs.json 3 | __dummy.html 4 | *.o 5 | *.obj 6 | __test__*__ 7 | profdump 8 | !source/profdump 9 | trace.* 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Anton Fediushin 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | profdump [![Page on DUB](https://img.shields.io/dub/v/profdump.svg)](http://code.dlang.org/packages/profdump) 2 | [![License](https://img.shields.io/dub/l/profdump.svg)](https://github.com/AntonMeep/profdump/blob/master/LICENSE) 3 | ============= 4 | profdump converts output of D programming language profiler into: 5 | - Plain text 6 | - JSON 7 | - DOT graph 8 | 9 | ## Why? 10 | Because profiler gives you [this](./example/simple.log). It's very hard to read and understand it. 11 | profdump can convert it to: 12 | - [plain text](./example/sample/simple.txt) 13 | - [json](./example/sample/simple.json) 14 | 15 | or just draw this beautiful graph: 16 | ![simple graph](./example/example.png?raw=true) 17 | 18 | ## Usage 19 | ``` 20 | Usage: profdump [options] [input file] [output file] 21 | Converts the output of dlang compiler into a plain text, json or dot graph. 22 | If input file is not specified, looks for 'trace.log' file. 23 | You can set input and output file to stdin/stdout by passing '-' instead of file name. 24 | 25 | Options: 26 | -p --plain print detailed information about functions (default) 27 | -j --json print JSON 28 | -d --dot output dot graph 29 | -b --blame print list of functions ordered by time 30 | -t --threshold (% of main function) hide functions below this threshold (default: 0.0) 31 | --pretty prettify JSON output (default: true) 32 | --colour customize colours of dot graph nodes (default: [0:"limegreen", 10:"slateblue", 50:"royalblue", 95:"red", 25:"steelblue", 75:"navy"]) 33 | -f --force overwrite output file if exists 34 | -v --verbose do not minimize function names 35 | -h --help This help information. 36 | ``` 37 | 38 | ## Graph output 39 | Every node represents a function and has the following layout: 40 | ``` 41 | +----------------------------+ 42 | | Function name | 43 | | total time % (self time %) | 44 | +----------------------------+ 45 | ``` 46 | 47 | And edge represents the calls between two functions and has the following layout: 48 | ``` 49 | calls 50 | parent --------------> child 51 | 52 | ``` 53 | 54 | ## Plain text output 55 | Prints everything. Basically converts `trace.log` making it easier to read. 56 | Check out examples: 57 | - [Simple example](./example/sample/simple.txt) 58 | - [Profdump's trace.log dump](./example/sample/profdump.txt) 59 | 60 | ## `--blame` option 61 | Prints functions ordered by time: 62 | ``` 63 | void main() 0.01277s 100.00% 64 | ulong example.fib(..) 0.01153s 90.25% 65 | int example.child2(..) 0.00012s 0.91% 66 | int example.child1(..) 0.00002s 0.12% 67 | int example.sum(..) 0.00000s 0.03% 68 | ``` 69 | 70 | ## JSON output 71 | Has the following layout: 72 | ``` 73 | { 74 | "functions": [ 75 | { 76 | "name": , // Demangled name of function 77 | "mangled": , // Mangled name of function 78 | "time": , // Time spent on this function and all its children in ticks 79 | "timeSec": , // Time spent on this function and all its children in seconds 80 | "functionTime": , // Time spent on this function in ticks 81 | "functionTimeSec": , // Time spent on this function in seconds 82 | "perc": , // Time spent on this function and all its children in % of main function time 83 | "functionPerc": , // Time spent on this function in % of main function time 84 | "callsTo": [ // All children which are called by this function 85 | { 86 | "name": , // Demangled name of children 87 | "mangled": , // Number of calls 89 | } 90 | <...> 91 | ], 92 | "calledBy": [ // All functions that call this function 93 | { 94 | "name": , // Demangled name of parent 95 | "mangled": , // Number of calls 97 | } 98 | <...> 99 | ] 100 | } 101 | <...> 102 | ], 103 | "tps": // Number of ticks per second 104 | } 105 | 106 | ``` 107 | -------------------------------------------------------------------------------- /dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "profdump", 3 | "authors": [ 4 | "Anton Fediushin" 5 | ], 6 | "description": "Converts profiler output to DOT graph, JSON or human-readable plain text", 7 | "copyright": "Copyright © 2017, Anton Fediusin", 8 | "license": "MIT", 9 | "dependencies": { 10 | "emsi_containers": "~>0.8.0-alpha.9" 11 | }, 12 | "configurations": [ 13 | { 14 | "name": "executable" 15 | }, 16 | { 17 | "name": "unittest", 18 | "dependencies": { 19 | "silly": {"path": "../silly"}, 20 | "fluent-asserts": "" 21 | } 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /dub.selections.json: -------------------------------------------------------------------------------- 1 | { 2 | "fileVersion": 1, 3 | "versions": { 4 | "emsi_containers": "0.8.0-alpha.9", 5 | "silly": {"path":"../silly"}, 6 | "stdx-allocator": "2.77.2" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | example 2 | example.o 3 | trace.def 4 | trace.log 5 | -------------------------------------------------------------------------------- /example/Makefile: -------------------------------------------------------------------------------- 1 | PROFDUMP := ../profdump 2 | DUB := dub 3 | SAMPLEDIR := sample/ 4 | SOURCES := profdump.log simple.log 5 | PLAIN := $(addprefix $(SAMPLEDIR), $(SOURCES:.log=.txt)) 6 | JSON := $(addprefix $(SAMPLEDIR), $(SOURCES:.log=.json)) 7 | DOT := $(addprefix $(SAMPLEDIR), $(SOURCES:.log=.dot)) 8 | SVG := $(addprefix $(SAMPLEDIR), $(SOURCES:.log=.svg)) 9 | 10 | 11 | .PHONY: dot svg plain json 12 | 13 | all: $(PROFDUMP) plain json dot svg 14 | 15 | plain: $(SAMPLEDIR) $(PLAIN) 16 | $(SAMPLEDIR)%.txt : %.log $(SAMPLEDIR) $(PROFDUMP) 17 | @echo Converting $< to $@ 18 | @$(PROFDUMP) -fp $< $@ 19 | 20 | json: $(SAMPLEDIR) $(JSON) 21 | $(SAMPLEDIR)%.json : %.log $(SAMPLEDIR) $(PROFDUMP) 22 | @echo Converting $< to $@ 23 | @$(PROFDUMP) -fj --pretty $< $@ 24 | 25 | dot: $(SAMPLEDIR) $(DOT) 26 | $(SAMPLEDIR)%.dot : %.log $(SAMPLEDIR) $(PROFDUMP) 27 | @echo Converting $< to $@ 28 | @$(PROFDUMP) -fd $< $@ 29 | 30 | svg: $(SAMPLEDIR) $(SVG) 31 | $(SAMPLEDIR)%.svg : $(SAMPLEDIR)%.dot 32 | @echo Converting $< to $@ 33 | @dot -Tsvg $< -o$@ 34 | 35 | $(SAMPLEDIR) : 36 | @mkdir $(SAMPLEDIR) 37 | 38 | $(PROFDUMP) : 39 | @cd ../ && $(DUB) build 40 | 41 | clean : 42 | @rm -rf $(SAMPLEDIR) 43 | -------------------------------------------------------------------------------- /example/example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AntonMeep/profdump/0e2564f19ca365c14f7b85f004bca2ecd0d57f90/example/example.png -------------------------------------------------------------------------------- /example/sample/simple.dot: -------------------------------------------------------------------------------- 1 | digraph { 2 | "_D7example6child1FiZi" [label="int example.child1(..) 3 | \n0.12%(0.12%)", shape="box", style=filled, fillcolor="limegreen"]; 4 | "_D7example3fibFmZm" [label="ulong example.fib(..) 5 | \n90.25%(90.25%)", shape="box", style=filled, fillcolor="navy"]; 6 | "_D7example3fibFmZm" -> "_D7example3fibFmZm" [label="266x"]; 7 | "_D7example3sumFiiZi" [label="int example.sum(..) 8 | \n0.03%(0.03%)", shape="box", style=filled, fillcolor="limegreen"]; 9 | "_D7example6child2FiZi" [label="int example.child2(..) 10 | \n0.91%(0.84%)", shape="box", style=filled, fillcolor="limegreen"]; 11 | "_D7example6child2FiZi" -> "_D7example6child1FiZi" [label="2x"]; 12 | "_Dmain" [label="void main() 13 | \n100.00%(8.77%)", shape="box", style=filled, fillcolor="red"]; 14 | "_Dmain" -> "_D7example6child1FiZi" [label="1x"]; 15 | "_Dmain" -> "_D7example3fibFmZm" [label="10x"]; 16 | "_Dmain" -> "_D7example3sumFiiZi" [label="10x"]; 17 | "_Dmain" -> "_D7example6child2FiZi" [label="1x"]; 18 | } 19 | -------------------------------------------------------------------------------- /example/sample/simple.json: -------------------------------------------------------------------------------- 1 | { 2 | "functions": [ 3 | { 4 | "calledBy": [ 5 | { 6 | "calls": 2, 7 | "mangled": "_D7example6child2FiZi", 8 | "name": "int example.child2(..)" 9 | }, 10 | { 11 | "calls": 1, 12 | "mangled": "_Dmain", 13 | "name": "void main()" 14 | } 15 | ], 16 | "functionPerc": 0.118130899965763092, 17 | "functionTime": 54, 18 | "functionTimeSec": 1.50857158587314188e-05, 19 | "mangled": "_D7example6child1FiZi", 20 | "name": "int example.child1(..)", 21 | "perc": 0.118130899965763092, 22 | "time": 54, 23 | "timeSec": 1.50857158587314188e-05 24 | }, 25 | { 26 | "calledBy": [ 27 | { 28 | "calls": 266, 29 | "mangled": "_D7example3fibFmZm", 30 | "name": "ulong example.fib(..)" 31 | }, 32 | { 33 | "calls": 10, 34 | "mangled": "_Dmain", 35 | "name": "void main()" 36 | } 37 | ], 38 | "callsTo": [ 39 | { 40 | "calls": 266, 41 | "mangled": "_D7example3fibFmZm", 42 | "name": "ulong example.fib(..)" 43 | } 44 | ], 45 | "functionPerc": 90.25201416015625, 46 | "functionTime": 41256, 47 | "functionTimeSec": 0.0115254875272512436, 48 | "mangled": "_D7example3fibFmZm", 49 | "name": "ulong example.fib(..)", 50 | "perc": 90.25201416015625, 51 | "time": 41256, 52 | "timeSec": 0.0115254875272512436 53 | }, 54 | { 55 | "calledBy": [ 56 | { 57 | "calls": 10, 58 | "mangled": "_Dmain", 59 | "name": "void main()" 60 | } 61 | ], 62 | "functionPerc": 0.0262513123452663422, 63 | "functionTime": 12, 64 | "functionTimeSec": 3.35238132720405702e-06, 65 | "mangled": "_D7example3sumFiiZi", 66 | "name": "int example.sum(..)", 67 | "perc": 0.0262513123452663422, 68 | "time": 12, 69 | "timeSec": 3.35238132720405702e-06 70 | }, 71 | { 72 | "calledBy": [ 73 | { 74 | "calls": 1, 75 | "mangled": "_Dmain", 76 | "name": "void main()" 77 | } 78 | ], 79 | "callsTo": [ 80 | { 81 | "calls": 2, 82 | "mangled": "_D7example6child1FiZi", 83 | "name": "int example.child1(..)" 84 | } 85 | ], 86 | "functionPerc": 0.835666775703430176, 87 | "functionTime": 382, 88 | "functionTimeSec": 0.000106717474409379065, 89 | "mangled": "_D7example6child2FiZi", 90 | "name": "int example.child2(..)", 91 | "perc": 0.914420723915100098, 92 | "time": 418, 93 | "timeSec": 0.000116774615889880806 94 | }, 95 | { 96 | "callsTo": [ 97 | { 98 | "calls": 1, 99 | "mangled": "_D7example6child1FiZi", 100 | "name": "int example.child1(..)" 101 | }, 102 | { 103 | "calls": 10, 104 | "mangled": "_D7example3fibFmZm", 105 | "name": "ulong example.fib(..)" 106 | }, 107 | { 108 | "calls": 10, 109 | "mangled": "_D7example3sumFiiZi", 110 | "name": "int example.sum(..)" 111 | }, 112 | { 113 | "calls": 1, 114 | "mangled": "_D7example6child2FiZi", 115 | "name": "int example.child2(..)" 116 | } 117 | ], 118 | "functionPerc": 8.76793861389160156, 119 | "functionTime": 4008, 120 | "functionTimeSec": 0.00111969537101686001, 121 | "mangled": "_Dmain", 122 | "name": "void main()", 123 | "perc": 100, 124 | "time": 45712, 125 | "timeSec": 0.0127703379839658737 126 | } 127 | ], 128 | "tps": 3579545 129 | } 130 | -------------------------------------------------------------------------------- /example/sample/simple.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | %3 11 | 12 | 13 | 14 | _D7example6child1FiZi 15 | 16 | int example.child1(..) 17 | 0.12%(0.12%) 18 | 19 | 20 | 21 | _D7example3fibFmZm 22 | 23 | ulong example.fib(..) 24 | 90.25%(90.25%) 25 | 26 | 27 | 28 | _D7example3fibFmZm->_D7example3fibFmZm 29 | 30 | 31 | 266x 32 | 33 | 34 | 35 | _D7example3sumFiiZi 36 | 37 | int example.sum(..) 38 | 0.03%(0.03%) 39 | 40 | 41 | 42 | _D7example6child2FiZi 43 | 44 | int example.child2(..) 45 | 0.91%(0.84%) 46 | 47 | 48 | 49 | _D7example6child2FiZi->_D7example6child1FiZi 50 | 51 | 52 | 2x 53 | 54 | 55 | 56 | _Dmain 57 | 58 | void main() 59 | 100.00%(8.77%) 60 | 61 | 62 | 63 | _Dmain->_D7example6child1FiZi 64 | 65 | 66 | 1x 67 | 68 | 69 | 70 | _Dmain->_D7example3fibFmZm 71 | 72 | 73 | 10x 74 | 75 | 76 | 77 | _Dmain->_D7example3sumFiiZi 78 | 79 | 80 | 10x 81 | 82 | 83 | 84 | _Dmain->_D7example6child2FiZi 85 | 86 | 87 | 1x 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /example/sample/simple.txt: -------------------------------------------------------------------------------- 1 | Function 'int example.child1(..)': 2 | Mangled name: '_D7example6child1FiZi' 3 | Called by: 4 | int example.child2(..) 2 times 5 | void main() 1 time 6 | Took: 0.000015 seconds (0.118131%) 7 | Finished in: 0.000015 seconds (0.118131%) 8 | Function 'ulong example.fib(..)': 9 | Mangled name: '_D7example3fibFmZm' 10 | Calls: 11 | ulong example.fib(..) 266 times 12 | Called by: 13 | ulong example.fib(..) 266 times 14 | void main() 10 times 15 | Took: 0.011525 seconds (90.252014%) 16 | Finished in: 0.011525 seconds (90.252014%) 17 | Function 'int example.sum(..)': 18 | Mangled name: '_D7example3sumFiiZi' 19 | Called by: 20 | void main() 10 times 21 | Took: 0.000003 seconds (0.026251%) 22 | Finished in: 0.000003 seconds (0.026251%) 23 | Function 'int example.child2(..)': 24 | Mangled name: '_D7example6child2FiZi' 25 | Calls: 26 | int example.child1(..) 2 times 27 | Called by: 28 | void main() 1 time 29 | Took: 0.000107 seconds (0.835667%) 30 | Finished in: 0.000117 seconds (0.914421%) 31 | Function 'void main()': 32 | Mangled name: '_Dmain' 33 | Calls: 34 | int example.child1(..) 1 time 35 | ulong example.fib(..) 10 times 36 | int example.sum(..) 10 times 37 | int example.child2(..) 1 time 38 | Took: 0.001120 seconds (8.767939%) 39 | Finished in: 0.012770 seconds (100.000000%) 40 | -------------------------------------------------------------------------------- /example/simple.log: -------------------------------------------------------------------------------- 1 | ------------------ 2 | 10 _Dmain 3 | 266 _D7example3fibFmZm 4 | _D7example3fibFmZm 276 41256 41256 5 | 266 _D7example3fibFmZm 6 | ------------------ 7 | 10 _Dmain 8 | _D7example3sumFiiZi 10 12 12 9 | ------------------ 10 | 1 _Dmain 11 | 2 _D7example6child2FiZi 12 | _D7example6child1FiZi 3 54 54 13 | ------------------ 14 | 1 _Dmain 15 | _D7example6child2FiZi 1 418 382 16 | 2 _D7example6child1FiZi 17 | ------------------ 18 | _Dmain 0 45712 4008 19 | 10 _D7example3fibFmZm 20 | 10 _D7example3sumFiiZi 21 | 1 _D7example6child1FiZi 22 | 1 _D7example6child2FiZi 23 | 24 | ======== Timer Is 3579545 Ticks/Sec, Times are in Microsecs ======== 25 | 26 | Num Tree Func Per 27 | Calls Time Time Call 28 | 29 | 276 11525 11525 41 ulong example.fib(ulong) 30 | 1 12770 1119 1119 _Dmain 31 | 1 116 106 106 int example.child2(int) 32 | 3 15 15 5 int example.child1(int) 33 | 10 3 3 0 int example.sum(int, int) 34 | -------------------------------------------------------------------------------- /profdump-test-unittest: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AntonMeep/profdump/0e2564f19ca365c14f7b85f004bca2ecd0d57f90/profdump-test-unittest -------------------------------------------------------------------------------- /source/app.d: -------------------------------------------------------------------------------- 1 | version(unittest) { 2 | 3 | } else { 4 | void main() { assert(0, "TODO"); } 5 | } -------------------------------------------------------------------------------- /source/profdump.d: -------------------------------------------------------------------------------- 1 | module profdump; 2 | 3 | import containers; 4 | 5 | alias HASH = ubyte[4]; 6 | 7 | import std.typecons : RefCounted; 8 | 9 | alias String = RefCounted!(DynamicArray!char); 10 | 11 | struct Function { 12 | private { 13 | String m_name; 14 | ulong m_calls; 15 | } 16 | 17 | this(string name, ulong calls = 0) { 18 | m_name.reserve(name.length); 19 | foreach(ref c; name) 20 | m_name ~= c; 21 | m_calls = calls; 22 | } 23 | 24 | this(String name, ulong calls = 0) { 25 | m_name = name; 26 | m_calls = calls; 27 | } 28 | 29 | @property: 30 | auto name() { return m_name; } 31 | void name(String m) { m_name = m; } 32 | 33 | auto calls() { return m_calls; } 34 | void calls(ulong c) { m_calls = c; } 35 | 36 | HASH hashOf() { 37 | import std.digest.murmurhash : MurmurHash3; 38 | import std.digest : digest; 39 | 40 | return m_name[].digest!(MurmurHash3!32); 41 | } 42 | } 43 | 44 | @("Function is alrighty") 45 | unittest { 46 | Function("_Dmain", 1).hashOf; 47 | } 48 | 49 | alias FuncMap = RefCounted!(HashMap!(HASH, Function)); 50 | 51 | struct Entity { 52 | private { 53 | Function m_function; 54 | FuncMap m_callsTo; 55 | FuncMap m_calledBy; 56 | ulong m_time; 57 | ulong m_total_time; 58 | } 59 | 60 | alias m_function this; 61 | 62 | @property: 63 | auto callsTo() { return m_callsTo; } 64 | void callsTo(typeof(m_callsTo) t) { m_callsTo = t; } 65 | void callsTo(Function f) 66 | in(f.hashOf !in m_callsTo) { 67 | m_callsTo[f.hashOf] = f; 68 | } 69 | 70 | auto calledBy() { return m_calledBy; } 71 | void calledBy(typeof(m_callsTo) t) { m_calledBy = t; } 72 | void calledBy(Function f) 73 | in(f.hashOf !in m_calledBy) { 74 | m_calledBy[f.hashOf] = f; 75 | } 76 | 77 | auto time() { return m_time; } 78 | void time(ulong t) { m_time = t; } 79 | 80 | auto totalTime() { return m_total_time; } 81 | void totalTime(ulong t) { m_total_time = t; } 82 | } 83 | --------------------------------------------------------------------------------