├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── examples ├── go15_stderr.log ├── godoc.sh └── read_input_file.sh ├── graph.go ├── http_server.go ├── http_server_test.go ├── main.go ├── parser.go ├── parser_test.go ├── subcommand.go ├── subcommand_test.go ├── template.go └── tracedata.go /.gitignore: -------------------------------------------------------------------------------- 1 | gcvis 2 | examples/gcvis 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - 1.8 4 | - 1.9 5 | 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, Dave Cheney 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | # This project is no longer maintained 4 | 5 | I'm sorry but I do not have the bandwidth to maintain this tool. Please do not send issues or PRs. Thank you. 6 |
7 | 8 | # gcvis 9 | 10 | Visualise Go program gctrace data in real time 11 | 12 | Note: GC timing graphs are only supported for go 1.6 13 | 14 | ## Usage 15 | 16 | Running it directly: 17 | 18 | ```bash 19 | env GOMAXPROCS=4 gcvis godoc -index -http=:6060 20 | ``` 21 | 22 | Adding the `gctrace` flag yourself: 23 | 24 | ```bash 25 | GODEBUG=gctrace=1 godoc -index -http=:6060 2>&1 | gcvis 26 | ``` 27 | 28 | Or from a log file: 29 | 30 | ```bash 31 | GODEBUG=gctrace=1 godoc -index -http=:6060 2> stderr.log 32 | cat stderr.log | gcvis 33 | ``` 34 | 35 | Starting the server without automatically opening a browser: 36 | 37 | ```bash 38 | gcvis -o=false godoc -index -http=:6060 39 | ``` 40 | -------------------------------------------------------------------------------- /examples/go15_stderr.log: -------------------------------------------------------------------------------- 1 | gc 1 @0.166s 0%: 0.22+2.3+0.074+0.76+0.56 ms clock, 0.45+2.3+0+0.55/0.59/0+1.1 ms cpu, 5->5->1 MB, 4 MB goal, 4 P 2 | gc 2 @0.191s 1%: 0.019+1.4+0.058+1.3+0.24 ms clock, 0.076+1.4+0+0.28/1.1/0+0.96 ms cpu, 4->4->1 MB, 5 MB goal, 4 P 3 | gc 3 @0.252s 1%: 0.018+2.1+0.074+2.1+0.22 ms clock, 0.075+2.1+0+0.68/2.0/0+0.91 ms cpu, 4->4->2 MB, 4 MB goal, 4 P 4 | gc 4 @0.280s 1%: 0.034+2.5+0.054+2.3+0.18 ms clock, 0.13+2.5+0+0.38/2.1/0+0.74 ms cpu, 4->5->4 MB, 4 MB goal, 4 P 5 | gc 5 @0.306s 2%: 0.021+3.3+0.20+1.7+0.23 ms clock, 0.087+3.3+0+1.5/1.5/0+0.93 ms cpu, 6->7->4 MB, 7 MB goal, 4 P 6 | gc 6 @0.369s 2%: 0.019+4.0+0.040+2.7+0.25 ms clock, 0.076+4.0+0+0.71/2.5/0+1.0 ms cpu, 7->8->5 MB, 7 MB goal, 4 P 7 | gc 7 @0.409s 2%: 0.024+5.1+0.057+2.5+0.27 ms clock, 0.096+5.1+0+1.6/2.3/0+1.1 ms cpu, 9->10->5 MB, 9 MB goal, 4 P 8 | gc 8 @0.472s 3%: 0.025+5.2+0.17+2.5+0.27 ms clock, 0.10+5.2+0+2.1/2.3/0+1.0 ms cpu, 9->10->7 MB, 10 MB goal, 4 P 9 | gc 9 @0.516s 3%: 0.024+5.9+0.041+2.2+0.27 ms clock, 0.098+5.9+0+2.8/2.1/0+1.1 ms cpu, 13->13->7 MB, 13 MB goal, 4 P 10 | gc 10 @0.555s 3%: 0.033+7.0+0.031+1.2+0.31 ms clock, 0.13+7.0+0+4.0/0.98/0+1.2 ms cpu, 13->14->10 MB, 13 MB goal, 4 P 11 | gc 11 @0.598s 4%: 0.036+6.8+0.045+1.0+0.36 ms clock, 0.14+6.8+0+4.4/0.87/0+1.4 ms cpu, 17->18->9 MB, 18 MB goal, 4 P 12 | gc 12 @0.634s 4%: 0.035+7.3+0.018+0.14+0.60 ms clock, 0.14+7.3+0+5.4/0.018/0+2.4 ms cpu, 17->18->10 MB, 17 MB goal, 4 P 13 | gc 13 @0.691s 4%: 0.047+7.0+0.019+0.17+0.35 ms clock, 0.18+7.0+0+5.6/0.027/0+1.4 ms cpu, 18->19->10 MB, 19 MB goal, 4 P 14 | gc 14 @0.720s 4%: 0.17+6.1+0.092+0.18+0.39 ms clock, 0.69+6.1+0+6.2/0.042/0+1.5 ms cpu, 18->19->10 MB, 19 MB goal, 4 P 15 | gc 15 @0.759s 5%: 0.034+6.3+0.087+0.20+0.49 ms clock, 0.13+6.3+0+6.0/0.041/0+1.9 ms cpu, 17->18->10 MB, 18 MB goal, 4 P 16 | gc 16 @0.802s 5%: 0.024+5.6+0.052+0.22+0.47 ms clock, 0.099+5.6+0+5.6/0.029/0.26+1.9 ms cpu, 18->18->10 MB, 18 MB goal, 4 P 17 | gc 17 @0.838s 5%: 0.12+6.7+0.32+2.6+0.46 ms clock, 0.51+6.7+0+8.2/2.3/0+1.8 ms cpu, 18->19->9 MB, 18 MB goal, 4 P 18 | gc 18 @0.868s 5%: 0.031+4.3+0.12+4.7+0.41 ms clock, 0.12+4.3+0+3.2/4.3/0+1.6 ms cpu, 16->17->7 MB, 16 MB goal, 4 P 19 | gc 19 @0.898s 5%: 0.055+3.2+0.22+2.7+0.37 ms clock, 0.22+3.2+0+3.2/2.4/0+1.4 ms cpu, 13->14->6 MB, 14 MB goal, 4 P 20 | gc 20 @0.931s 5%: 0.064+1.9+0.033+3.0+0.28 ms clock, 0.25+1.9+0+2.4/2.6/0+1.1 ms cpu, 11->12->5 MB, 11 MB goal, 4 P 21 | gc 21 @0.951s 5%: 0.030+1.3+0.051+3.7+0.19 ms clock, 0.12+1.3+0+1.5/3.5/0+0.79 ms cpu, 8->8->3 MB, 8 MB goal, 4 P 22 | gc 22 @0.965s 6%: 0.033+0.91+0.064+3.0+0.19 ms clock, 0.13+0.91+0+1.7/2.9/0+0.78 ms cpu, 5->6->3 MB, 5 MB goal, 4 P 23 | gc 23 @0.978s 6%: 0.031+0.63+0.017+3.5+0.21 ms clock, 0.12+0.63+0+1.2/3.3/0+0.87 ms cpu, 4->5->3 MB, 4 MB goal, 4 P 24 | gc 24 @0.988s 6%: 0.14+0.76+0.041+3.4+0.18 ms clock, 0.57+0.76+0+2.3/3.1/0.12+0.72 ms cpu, 4->5->3 MB, 4 MB goal, 4 P 25 | gc 25 @0.996s 6%: 0.024+0.52+0.028+2.4+0.31 ms clock, 0.098+0.52+0+0.36/2.3/2.4+1.2 ms cpu, 4->4->2 MB, 4 MB goal, 4 P 26 | gc 26 @1.028s 6%: 0.024+0.46+0.022+3.6+0.38 ms clock, 0.097+0.46+0+0.48/3.5/0+1.5 ms cpu, 4->4->2 MB, 4 MB goal, 4 P 27 | gc 27 @1.079s 6%: 0.031+0.47+0.094+1.7+0.19 ms clock, 0.12+0.47+0+0.005/1.6/3.7+0.77 ms cpu, 4->4->2 MB, 4 MB goal, 4 P 28 | gc 28 @1.138s 5%: 0.048+0.42+0.002+2.6+0.16 ms clock, 0.19+0.42+0+0.17/2.4/4.4+0.67 ms cpu, 5->7->5 MB, 4 MB goal, 4 P 29 | gc 29 @1.144s 5%: 0.26+0.27+0.26+1.4+0.28 ms clock, 1.0+0.27+0+0/1.3/2.5+1.1 ms cpu, 9->9->6 MB, 6 MB goal, 4 P 30 | gc 30 @1.296s 5%: 0.049+0.41+0.012+1.8+0.35 ms clock, 0.19+0.41+0+0/1.7/3.4+1.4 ms cpu, 9->9->2 MB, 12 MB goal, 4 P 31 | gc 31 @1.340s 5%: 0.074+0.31+0.002+1.0+0.20 ms clock, 0.29+0.31+0+0.062/0.99/1.7+0.81 ms cpu, 4->4->1 MB, 5 MB goal, 4 P 32 | gc 32 @1.362s 5%: 0.31+1.1+0.21+1.0+0.34 ms clock, 1.2+1.1+0+1.2/0.78/0.79+1.3 ms cpu, 4->5->2 MB, 4 MB goal, 4 P 33 | gc 33 @1.366s 5%: 0.10+0.29+0.26+1.1+0.18 ms clock, 0.41+0.29+0+0.46/0.97/1.1+0.72 ms cpu, 4->4->1 MB, 5 MB goal, 4 P 34 | gc 34 @1.376s 5%: 0.032+2.3+0.043+0.32+0.27 ms clock, 0.12+2.3+0+3.5/0.18/0+1.1 ms cpu, 4->4->2 MB, 5 MB goal, 4 P 35 | gc 35 @1.381s 5%: 0.063+0.96+1.1+2.1+0.26 ms clock, 0.25+0.96+0+2.2/2.0/0+1.0 ms cpu, 4->5->2 MB, 5 MB goal, 4 P 36 | gc 36 @1.388s 5%: 0.023+2.0+0.097+2.0+0.31 ms clock, 0.095+2.0+0+2.7/1.6/0+1.2 ms cpu, 4->5->4 MB, 6 MB goal, 4 P 37 | gc 37 @1.394s 5%: 0.033+2.1+0.016+1.2+0.30 ms clock, 0.13+2.1+0+3.6/1.1/0+1.2 ms cpu, 5->6->4 MB, 4 MB goal, 4 P 38 | gc 38 @1.399s 5%: 0.021+4.8+0.017+0.27+0.63 ms clock, 0.087+4.8+0+4.8/0.004/0.021+2.5 ms cpu, 5->6->4 MB, 6 MB goal, 4 P 39 | gc 39 @1.405s 6%: 0.077+3.9+0.013+0.30+0.64 ms clock, 0.31+3.9+0+4.9/0.025/0.003+2.5 ms cpu, 5->7->5 MB, 7 MB goal, 4 P 40 | gc 40 @1.412s 6%: 0.020+4.5+0.010+0.12+0.58 ms clock, 0.080+4.5+0+5.6/0.001/0.025+2.3 ms cpu, 6->8->7 MB, 8 MB goal, 4 P 41 | gc 41 @1.418s 6%: 0.022+4.7+0.10+0.22+0.67 ms clock, 0.091+4.7+0+6.1/0.12/0+2.7 ms cpu, 8->10->7 MB, 10 MB goal, 4 P 42 | gc 42 @1.427s 6%: 0.43+4.9+0.001+0.20+0.55 ms clock, 1.7+4.9+0+5.8/0.007/0.11+2.2 ms cpu, 11->12->9 MB, 11 MB goal, 4 P 43 | gc 43 @1.438s 6%: 0.040+6.0+0.009+0.17+1.1 ms clock, 0.16+6.0+0+6.5/0.011/0+4.5 ms cpu, 16->17->11 MB, 16 MB goal, 4 P 44 | gc 44 @1.452s 7%: 0.10+6.3+0.018+0.14+0.81 ms clock, 0.42+6.3+0+5.9/0.015/0+3.2 ms cpu, 20->21->11 MB, 21 MB goal, 4 P 45 | gc 45 @1.466s 7%: 0.045+6.1+0.067+0.18+0.69 ms clock, 0.18+6.1+0+6.9/0/0.17+2.7 ms cpu, 19->20->10 MB, 20 MB goal, 4 P 46 | gc 46 @1.480s 7%: 0.037+5.8+0.077+0.24+0.83 ms clock, 0.15+5.8+0+8.7/0/0.016+3.3 ms cpu, 17->19->9 MB, 18 MB goal, 4 P 47 | gc 47 @1.491s 7%: 0.040+5.7+0.060+0.10+0.65 ms clock, 0.16+5.7+0+7.2/0.001/0.11+2.6 ms cpu, 15->16->9 MB, 16 MB goal, 4 P 48 | gc 48 @1.505s 7%: 0.031+5.6+0.12+0.15+0.71 ms clock, 0.12+5.6+0+7.6/0.012/0.16+2.8 ms cpu, 16->17->8 MB, 17 MB goal, 4 P 49 | gc 49 @1.520s 8%: 0.088+6.5+0.30+0.24+0.79 ms clock, 0.35+6.5+0+8.4/0.017/0.080+3.1 ms cpu, 14->16->11 MB, 15 MB goal, 4 P 50 | gc 50 @1.533s 8%: 0.061+5.1+0.16+0.25+0.64 ms clock, 0.24+5.1+0+8.3/0.020/0.057+2.5 ms cpu, 19->21->11 MB, 20 MB goal, 4 P 51 | gc 51 @1.544s 8%: 0.048+4.4+0.016+0.11+0.39 ms clock, 0.19+4.4+0+8.1/0.003/0.076+1.5 ms cpu, 17->18->8 MB, 17 MB goal, 4 P 52 | gc 52 @1.553s 8%: 0.054+4.2+0.16+0.66+0.38 ms clock, 0.21+4.2+0+7.9/0.48/0+1.5 ms cpu, 13->14->7 MB, 13 MB goal, 4 P 53 | gc 53 @1.563s 8%: 0.071+2.9+0.22+1.2+0.43 ms clock, 0.28+2.9+0+6.7/1.1/0+1.7 ms cpu, 12->13->6 MB, 13 MB goal, 4 P 54 | gc 54 @1.573s 9%: 0.054+2.2+0.15+1.6+4.9 ms clock, 0.21+2.2+0+5.7/1.6/0+19 ms cpu, 10->11->5 MB, 10 MB goal, 4 P 55 | gc 55 @1.586s 9%: 0.069+4.5+1.3+0.48+0.41 ms clock, 0.27+4.5+0+9.8/0.26/0+1.6 ms cpu, 8->9->6 MB, 8 MB goal, 4 P 56 | gc 56 @1.598s 9%: 0.039+2.7+0.043+1.0+0.28 ms clock, 0.15+2.7+0+6.8/1.0/0+1.1 ms cpu, 10->11->5 MB, 10 MB goal, 4 P 57 | gc 57 @1.605s 9%: 0.062+0.95+0.037+2.6+0.17 ms clock, 0.25+0.95+0+3.6/2.6/0.52+0.68 ms cpu, 8->9->4 MB, 8 MB goal, 4 P 58 | gc 58 @1.611s 10%: 4.5+0.74+0.061+5.9+0.30 ms clock, 18+0.74+0+5.0/5.4/0.002+1.2 ms cpu, 6->7->4 MB, 6 MB goal, 4 P 59 | gc 59 @1.627s 10%: 0.032+0.28+0.46+4.2+0.19 ms clock, 0.13+0.28+0+1.7/4.0/0.013+0.76 ms cpu, 6->6->3 MB, 6 MB goal, 4 P 60 | gc 60 @1.636s 10%: 0.043+1.2+0.018+1.5+0.18 ms clock, 0.17+1.2+0+1.3/1.4/2.1+0.72 ms cpu, 6->7->4 MB, 5 MB goal, 4 P 61 | gc 61 @1.642s 10%: 0.079+0.33+0.66+2.3+0.17 ms clock, 0.31+0.33+0+0/2.1/5.0+0.71 ms cpu, 10->10->7 MB, 7 MB goal, 4 P 62 | gc 62 @1.672s 10%: 0.053+1.4+1.3+1.6+0.38 ms clock, 0.21+1.4+0+4.1/1.6/0+1.5 ms cpu, 12->13->2 MB, 14 MB goal, 4 P 63 | gc 63 @1.681s 10%: 0.048+5.0+0.012+2.8+1.0 ms clock, 0.19+5.0+0+1.8/2.7/0+4.2 ms cpu, 4->5->3 MB, 4 MB goal, 4 P 64 | gc 64 @1.693s 10%: 0.29+9.1+0.027+0.17+0.67 ms clock, 1.1+9.1+0+7.3/0.006/0.14+2.7 ms cpu, 4->5->4 MB, 4 MB goal, 4 P 65 | gc 65 @1.707s 10%: 0.030+11+0.009+0.10+0.63 ms clock, 0.12+11+0+8.4/0.007/0.13+2.5 ms cpu, 5->6->4 MB, 5 MB goal, 4 P 66 | gc 66 @1.724s 10%: 0.030+15+0.001+0.093+0.73 ms clock, 0.12+15+0+8.8/0.006/0.22+2.9 ms cpu, 6->7->5 MB, 6 MB goal, 4 P 67 | gc 67 @1.745s 11%: 0.068+20+0.001+0.35+1.1 ms clock, 0.27+20+0+11/0.006/0.53+4.5 ms cpu, 8->9->6 MB, 8 MB goal, 4 P 68 | gc 68 @1.774s 11%: 0.026+28+0.001+0.12+1.0 ms clock, 0.10+28+0+12/0.017/0.63+4.0 ms cpu, 9->11->8 MB, 9 MB goal, 4 P 69 | gc 69 @1.877s 11%: 0.033+39+0.002+0.13+0.98 ms clock, 0.13+39+0+16/0.004/6.3+3.9 ms cpu, 13->14->9 MB, 13 MB goal, 4 P 70 | gc 70 @1.941s 12%: 0.052+30+0.001+0.12+0.56 ms clock, 0.20+30+0+12/0.019/0.68+2.2 ms cpu, 17->18->10 MB, 18 MB goal, 4 P 71 | gc 71 @2.032s 12%: 0.079+35+0.10+0.19+0.62 ms clock, 0.31+35+0+7.7/0.015/10+2.5 ms cpu, 18->18->10 MB, 18 MB goal, 4 P 72 | gc 72 @2.081s 12%: 0.056+29+0.15+0.13+0.51 ms clock, 0.22+29+0+11/0.006/4.1+2.0 ms cpu, 18->19->10 MB, 18 MB goal, 4 P 73 | gc 73 @2.165s 12%: 0.039+35+0.001+0.11+0.53 ms clock, 0.15+35+0+4.2/0.014/17+2.1 ms cpu, 18->19->11 MB, 19 MB goal, 4 P 74 | gc 74 @2.224s 12%: 0.040+29+0.001+0.25+0.46 ms clock, 0.16+29+0+14/0.006/0.71+1.8 ms cpu, 19->20->11 MB, 20 MB goal, 4 P 75 | gc 75 @2.305s 12%: 0.040+41+0.008+0.16+0.52 ms clock, 0.16+41+0+3.6/0.004/20+2.1 ms cpu, 20->21->11 MB, 21 MB goal, 4 P 76 | gc 76 @2.356s 12%: 0.059+38+0.15+0.18+0.60 ms clock, 0.23+38+0+2.8/0.010/21+2.4 ms cpu, 20->21->11 MB, 21 MB goal, 4 P 77 | gc 77 @2.456s 12%: 0.055+41+0.001+0.13+0.97 ms clock, 0.22+41+0+2.9/0.001/27+3.8 ms cpu, 21->21->13 MB, 21 MB goal, 4 P 78 | gc 78 @2.570s 12%: 0.060+29+0.17+1.4+0.46 ms clock, 0.24+29+0+16/1.2/13+1.8 ms cpu, 25->25->16 MB, 25 MB goal, 4 P 79 | gc 79 @2.625s 12%: 0.069+29+0.032+0.17+0.75 ms clock, 0.27+29+0+20/0.008/10+3.0 ms cpu, 29->30->16 MB, 30 MB goal, 4 P 80 | gc 80 @2.666s 13%: 0.071+34+0.001+0.11+0.48 ms clock, 0.28+34+0+1.6/0.016/40+1.9 ms cpu, 30->31->16 MB, 31 MB goal, 4 P 81 | gc 81 @2.835s 12%: 0.064+34+0.001+0.24+0.48 ms clock, 0.25+34+0+10/0.005/22+1.9 ms cpu, 30->31->14 MB, 31 MB goal, 4 P 82 | gc 82 @2.884s 12%: 0.066+30+0.001+0.098+0.49 ms clock, 0.26+30+0+17/0.008/7.3+1.9 ms cpu, 26->27->14 MB, 27 MB goal, 4 P 83 | gc 83 @2.924s 13%: 0.068+39+0.002+0.13+1.0 ms clock, 0.27+39+0+2.7/0.010/30+4.0 ms cpu, 25->26->14 MB, 26 MB goal, 4 P 84 | gc 84 @3.050s 13%: 0.071+29+0.001+0.11+0.48 ms clock, 0.28+29+0+19/0.015/5.4+1.9 ms cpu, 26->27->14 MB, 26 MB goal, 4 P 85 | gc 85 @3.091s 13%: 0.072+40+0.12+0.13+0.65 ms clock, 0.28+40+0+2.0/0.037/33+2.6 ms cpu, 27->28->15 MB, 27 MB goal, 4 P 86 | gc 86 @3.141s 13%: 0.085+34+0.080+0.096+0.51 ms clock, 0.34+34+0+11/0.006/25+2.0 ms cpu, 27->28->15 MB, 28 MB goal, 4 P 87 | gc 87 @3.246s 13%: 0.083+34+0.001+0.19+0.63 ms clock, 0.33+34+0+19/0.011/12+2.5 ms cpu, 27->28->15 MB, 28 MB goal, 4 P 88 | gc 88 @3.298s 13%: 0.10+33+0.003+0.18+0.73 ms clock, 0.43+33+0+16/0.009/17+2.9 ms cpu, 29->30->16 MB, 28 MB goal, 4 P 89 | gc 89 @3.361s 13%: 0.058+32+0.16+0.093+0.54 ms clock, 0.23+32+0+15/0.001/22+2.1 ms cpu, 30->31->18 MB, 31 MB goal, 4 P 90 | gc 90 @3.489s 13%: 0.069+33+0.11+0.19+0.68 ms clock, 0.27+33+0+16/0.004/21+2.7 ms cpu, 34->34->17 MB, 34 MB goal, 4 P 91 | gc 91 @3.571s 13%: 0.062+39+0.19+0.10+0.63 ms clock, 0.24+39+0+18/0.008/42+2.5 ms cpu, 32->33->23 MB, 33 MB goal, 4 P 92 | gc 92 @3.782s 13%: 0.063+33+0.083+3.2+0.52 ms clock, 0.25+33+0+16/3.1/33+2.0 ms cpu, 43->43->23 MB, 44 MB goal, 4 P 93 | gc 93 @4.105s 12%: 0.068+36+0.079+0.096+0.53 ms clock, 0.27+36+0+10/0/35+2.1 ms cpu, 43->44->19 MB, 45 MB goal, 4 P 94 | gc 94 @4.185s 12%: 0.078+32+4.6+0.10+0.84 ms clock, 0.31+32+0+27/0.006/31+3.3 ms cpu, 36->38->28 MB, 36 MB goal, 4 P 95 | gc 95 @4.346s 12%: 0.074+35+1.0+0.20+2.3 ms clock, 0.29+35+0+32/0.028/31+9.5 ms cpu, 51->52->32 MB, 53 MB goal, 4 P 96 | gc 96 @4.558s 12%: 0.13+37+0.52+0.098+0.73 ms clock, 0.52+37+0+0.87/0.007/75+2.9 ms cpu, 61->62->30 MB, 62 MB goal, 4 P 97 | gc 97 @4.913s 11%: 0.096+36+0.001+0.097+0.55 ms clock, 0.38+36+0+15/0.004/34+2.2 ms cpu, 57->58->23 MB, 58 MB goal, 4 P 98 | gc 98 @5.043s 11%: 0.087+45+0.001+0.13+1.1 ms clock, 0.34+45+0+25/0.016/58+4.5 ms cpu, 44->45->29 MB, 45 MB goal, 4 P 99 | gc 99 @5.225s 11%: 0.076+43+0.011+0.16+0.81 ms clock, 0.30+43+0+24/0.018/59+3.2 ms cpu, 55->56->33 MB, 56 MB goal, 4 P 100 | gc 100 @5.424s 11%: 0.083+35+0.32+9.6+0.66 ms clock, 0.33+35+0+18/9.5/54+2.6 ms cpu, 64->65->34 MB, 64 MB goal, 4 P 101 | gc 101 @5.811s 11%: 0.10+31+0.019+0.094+0.55 ms clock, 0.42+31+0+27/0.007/17+2.2 ms cpu, 64->65->25 MB, 66 MB goal, 4 P 102 | gc 102 @5.991s 10%: 0.087+36+0.001+0.094+0.54 ms clock, 0.35+36+0+1.2/0.001/58+2.1 ms cpu, 48->49->25 MB, 50 MB goal, 4 P 103 | gc 103 @6.105s 10%: 0.075+38+0.001+0.12+0.66 ms clock, 0.30+38+0+20/0.015/44+2.6 ms cpu, 48->49->25 MB, 49 MB goal, 4 P 104 | gc 104 @6.189s 10%: 0.12+33+0.001+0.67+0.92 ms clock, 0.48+33+0+7.1/0.45/55+3.6 ms cpu, 48->48->25 MB, 49 MB goal, 4 P 105 | gc 105 @6.295s 11%: 0.13+43+0.017+0.20+0.63 ms clock, 0.55+43+0+17/0.004/65+2.5 ms cpu, 49->50->36 MB, 50 MB goal, 4 P 106 | gc 106 @6.488s 11%: 0.093+32+0.056+21+0.93 ms clock, 0.37+32+0+20/21/67+3.7 ms cpu, 70->71->50 MB, 71 MB goal, 4 P 107 | gc 107 @6.802s 10%: 0.10+41+0.77+10+0.70 ms clock, 0.41+41+0+24/9.9/71+2.8 ms cpu, 96->97->48 MB, 98 MB goal, 4 P 108 | gc 108 @7.046s 10%: 0.11+48+0.28+0.36+0.59 ms clock, 0.44+48+0+16/0.29/96+2.3 ms cpu, 92->94->50 MB, 94 MB goal, 4 P 109 | gc 109 @7.323s 10%: 0.10+35+14+0.11+1.0 ms clock, 0.41+35+0+40/0.017/63+4.1 ms cpu, 95->96->52 MB, 97 MB goal, 4 P 110 | gc 110 @8.033s 9%: 0.10+36+0.001+0.25+0.83 ms clock, 0.42+36+0+26/0.018/31+3.3 ms cpu, 99->100->35 MB, 101 MB goal, 4 P 111 | gc 111 @8.180s 9%: 0.12+50+0.001+0.11+0.63 ms clock, 0.48+50+0+28/0.001/75+2.5 ms cpu, 65->67->38 MB, 67 MB goal, 4 P 112 | gc 112 @8.515s 9%: 0.12+48+0.002+0.17+1.2 ms clock, 0.50+48+0+26/0.005/55+4.9 ms cpu, 73->74->36 MB, 74 MB goal, 4 P 113 | gc 113 @8.698s 9%: 0.12+39+0.12+0.098+0.68 ms clock, 0.48+39+0+22/0.013/50+2.7 ms cpu, 68->69->37 MB, 70 MB goal, 4 P 114 | gc 114 @8.815s 9%: 0.12+47+0.14+0.17+0.59 ms clock, 0.51+47+0+19/0.006/66+2.3 ms cpu, 70->75->38 MB, 72 MB goal, 4 P 115 | gc 115 @8.952s 9%: 0.12+36+0.089+3.0+0.87 ms clock, 0.49+36+0+33/2.7/40+3.5 ms cpu, 65->66->39 MB, 67 MB goal, 4 P 116 | gc 116 @9.101s 9%: 0.10+42+0.001+0.11+0.62 ms clock, 0.43+42+0+25/0.016/64+2.5 ms cpu, 73->75->41 MB, 75 MB goal, 4 P 117 | gc 117 @9.274s 9%: 0.12+40+0.008+0.23+0.61 ms clock, 0.48+40+0+27/0.058/76+2.4 ms cpu, 78->79->43 MB, 80 MB goal, 4 P 118 | gc 118 @9.424s 9%: 0.11+37+2.0+3.1+0.96 ms clock, 0.47+37+0+32/3.1/59+3.8 ms cpu, 63->65->42 MB, 65 MB goal, 4 P 119 | gc 119 @9.591s 9%: 0.13+40+0.001+0.29+0.61 ms clock, 0.53+40+0+33/0.14/73+2.4 ms cpu, 80->82->43 MB, 82 MB goal, 4 P 120 | gc 120 @9.749s 10%: 0.11+42+0.58+7.7+0.62 ms clock, 0.44+42+0+35/7.6/67+2.5 ms cpu, 96->96->52 MB, 84 MB goal, 4 P 121 | gc 121 @9.939s 9%: 0.10+51+0.001+0.23+0.62 ms clock, 0.42+51+0+5.8/0.048/111+2.5 ms cpu, 91->96->57 MB, 104 MB goal, 4 P 122 | gc 122 @10.093s 10%: 0.26+30+0.042+32+0.57 ms clock, 1.0+30+0+10/32/84+2.2 ms cpu, 97->103->59 MB, 106 MB goal, 4 P 123 | gc 123 @10.298s 9%: 0.10+40+0.14+7.5+0.79 ms clock, 0.43+40+0+8.4/7.4/116+3.1 ms cpu, 105->106->56 MB, 108 MB goal, 4 P 124 | gc 124 @10.504s 10%: 0.51+52+0.001+0.10+0.84 ms clock, 2.0+52+0+43/0.002/99+3.3 ms cpu, 109->111->60 MB, 112 MB goal, 4 P 125 | gc 125 @10.688s 10%: 0.15+27+1.7+27+0.57 ms clock, 0.62+27+0+36/27/79+2.2 ms cpu, 114->115->60 MB, 116 MB goal, 4 P 126 | gc 126 @10.907s 10%: 0.11+42+1.2+11+0.64 ms clock, 0.45+42+0+48/10/106+2.5 ms cpu, 115->117->63 MB, 118 MB goal, 4 P 127 | gc 127 @11.135s 10%: 0.10+45+0.030+10+0.81 ms clock, 0.40+45+0+8.8/10/151+3.2 ms cpu, 119->119->63 MB, 122 MB goal, 4 P 128 | gc 128 @11.267s 10%: 0.14+83+0.001+0.13+0.60 ms clock, 0.57+83+0+72/0.009/161+2.4 ms cpu, 122->124->68 MB, 125 MB goal, 4 P 129 | gc 129 @11.403s 10%: 0.10+40+10+12+0.70 ms clock, 0.43+40+0+51/12/122+2.8 ms cpu, 128->130->68 MB, 131 MB goal, 4 P 130 | gc 130 @11.543s 10%: 0.10+45+1.2+16+0.74 ms clock, 0.42+45+0+53/16/122+2.9 ms cpu, 128->131->69 MB, 132 MB goal, 4 P 131 | -------------------------------------------------------------------------------- /examples/godoc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Compiling gcvis 4 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 5 | go build -o $DIR/gcvis $DIR/../*.go 6 | 7 | gcvis godoc -index -http=:6060 8 | -------------------------------------------------------------------------------- /examples/read_input_file.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Compiling gcvis 4 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 5 | go build -o $DIR/gcvis $DIR/../*.go 6 | 7 | cat $DIR/go15_stderr.log | $DIR/gcvis 8 | -------------------------------------------------------------------------------- /graph.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "html/template" 5 | "io" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | type graphPoints [2]float64 11 | 12 | type Graph struct { 13 | Title string 14 | HeapUse, ScvgInuse, ScvgIdle []graphPoints 15 | ScvgSys, ScvgReleased, ScvgConsumed []graphPoints 16 | STWSclock []graphPoints 17 | MASclock []graphPoints 18 | STWMclock []graphPoints 19 | STWScpu []graphPoints 20 | MASAssistcpu []graphPoints 21 | MASBGcpu []graphPoints 22 | MASIdlecpu []graphPoints 23 | STWMcpu []graphPoints 24 | Tmpl *template.Template `json:"-"` 25 | mu sync.RWMutex `json:"-"` 26 | } 27 | 28 | var StartTime = time.Now() 29 | 30 | func NewGraph(title, tmpl string) Graph { 31 | g := Graph{ 32 | Title: title, 33 | HeapUse: []graphPoints{}, 34 | ScvgInuse: []graphPoints{}, 35 | ScvgIdle: []graphPoints{}, 36 | ScvgSys: []graphPoints{}, 37 | ScvgReleased: []graphPoints{}, 38 | ScvgConsumed: []graphPoints{}, 39 | STWSclock: []graphPoints{}, 40 | MASclock: []graphPoints{}, 41 | STWMclock: []graphPoints{}, 42 | STWScpu: []graphPoints{}, 43 | MASAssistcpu: []graphPoints{}, 44 | MASBGcpu: []graphPoints{}, 45 | MASIdlecpu: []graphPoints{}, 46 | STWMcpu: []graphPoints{}, 47 | } 48 | g.setTmpl(tmpl) 49 | 50 | return g 51 | } 52 | 53 | func (g *Graph) setTmpl(tmplStr string) { 54 | g.Tmpl = template.Must(template.New("vis").Parse(tmplStr)) 55 | } 56 | 57 | func (g *Graph) Write(w io.Writer) error { 58 | g.mu.RLock() 59 | defer g.mu.RUnlock() 60 | return g.Tmpl.Execute(w, g) 61 | } 62 | 63 | func (g *Graph) AddGCTraceGraphPoint(gcTrace *gctrace) { 64 | g.mu.RLock() 65 | defer g.mu.RUnlock() 66 | var elapsedTime float64 67 | if gcTrace.ElapsedTime == 0 { 68 | elapsedTime = time.Now().Sub(StartTime).Seconds() 69 | } else { 70 | elapsedTime = gcTrace.ElapsedTime 71 | } 72 | g.HeapUse = append(g.HeapUse, graphPoints{elapsedTime, float64(gcTrace.Heap1)}) 73 | g.STWSclock = append(g.STWSclock, graphPoints{elapsedTime, float64(gcTrace.STWSclock)}) 74 | g.MASclock = append(g.MASclock, graphPoints{elapsedTime, float64(gcTrace.MASclock)}) 75 | g.STWMclock = append(g.STWMclock, graphPoints{elapsedTime, float64(gcTrace.STWMclock)}) 76 | g.STWScpu = append(g.STWScpu, graphPoints{elapsedTime, float64(gcTrace.STWScpu)}) 77 | g.MASAssistcpu = append(g.MASAssistcpu, graphPoints{elapsedTime, float64(gcTrace.MASAssistcpu)}) 78 | g.MASBGcpu = append(g.MASBGcpu, graphPoints{elapsedTime, float64(gcTrace.MASBGcpu)}) 79 | g.MASIdlecpu = append(g.MASIdlecpu, graphPoints{elapsedTime, float64(gcTrace.MASIdlecpu)}) 80 | g.STWMcpu = append(g.STWMcpu, graphPoints{elapsedTime, float64(gcTrace.STWMcpu)}) 81 | } 82 | 83 | func (g *Graph) AddScavengerGraphPoint(scvg *scvgtrace) { 84 | g.mu.RLock() 85 | defer g.mu.RUnlock() 86 | var elapsedTime float64 87 | if scvg.ElapsedTime == 0 { 88 | elapsedTime = time.Now().Sub(StartTime).Seconds() 89 | } else { 90 | elapsedTime = scvg.ElapsedTime 91 | } 92 | g.ScvgInuse = append(g.ScvgInuse, graphPoints{elapsedTime, float64(scvg.inuse)}) 93 | g.ScvgIdle = append(g.ScvgIdle, graphPoints{elapsedTime, float64(scvg.idle)}) 94 | g.ScvgSys = append(g.ScvgSys, graphPoints{elapsedTime, float64(scvg.sys)}) 95 | g.ScvgReleased = append(g.ScvgReleased, graphPoints{elapsedTime, float64(scvg.released)}) 96 | g.ScvgConsumed = append(g.ScvgConsumed, graphPoints{elapsedTime, float64(scvg.consumed)}) 97 | } 98 | -------------------------------------------------------------------------------- /http_server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "log" 7 | "net" 8 | "net/http" 9 | "sync" 10 | "time" 11 | ) 12 | 13 | type HttpServer struct { 14 | graph *Graph 15 | listener net.Listener 16 | iface string 17 | port string 18 | 19 | listenerMtx sync.Mutex 20 | } 21 | 22 | func NewHttpServer(iface string, port string, graph *Graph) *HttpServer { 23 | h := &HttpServer{ 24 | graph: graph, 25 | iface: iface, 26 | port: port, 27 | } 28 | 29 | return h 30 | } 31 | 32 | func (h *HttpServer) Start() { 33 | serveMux := http.NewServeMux() 34 | 35 | serveMux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) { 36 | h.graph.Write(w) 37 | }) 38 | 39 | serveMux.HandleFunc("/graph.json", func(w http.ResponseWriter, req *http.Request) { 40 | w.Header().Set("Content-Type", "application/json") 41 | encoder := json.NewEncoder(w) 42 | if err := encoder.Encode(h.graph); err != nil { 43 | log.Fatalf("An error occurred while serving JSON endpoint: %v", err) 44 | } 45 | }) 46 | 47 | server := http.Server{ 48 | Handler: serveMux, 49 | ReadTimeout: 10 * time.Second, 50 | WriteTimeout: 10 * time.Second, 51 | } 52 | 53 | server.Serve(h.Listener()) 54 | } 55 | 56 | func (h *HttpServer) Close() { 57 | h.Listener().Close() 58 | } 59 | 60 | func (h *HttpServer) Url() string { 61 | return fmt.Sprintf("http://%s/", h.Listener().Addr()) 62 | } 63 | 64 | func (h *HttpServer) Listener() net.Listener { 65 | h.listenerMtx.Lock() 66 | defer h.listenerMtx.Unlock() 67 | 68 | if h.listener != nil { 69 | return h.listener 70 | } 71 | 72 | ifaceAndPort := fmt.Sprintf("%v:%v", h.iface, h.port) 73 | listener, err := net.Listen("tcp4", ifaceAndPort) 74 | if err != nil { 75 | log.Fatal(err) 76 | } 77 | 78 | h.listener = listener 79 | return h.listener 80 | } 81 | -------------------------------------------------------------------------------- /http_server_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "io/ioutil" 7 | "net/http" 8 | "strings" 9 | "testing" 10 | ) 11 | 12 | func TestHttpServerListener(t *testing.T) { 13 | graph := NewGraph("fake title", GCVIS_TMPL) 14 | server := NewHttpServer("127.0.0.1", "0", &graph) 15 | 16 | url := server.Url() 17 | 18 | if !strings.Contains(url, "http://127.0.0.1") { 19 | t.Fatalf("Server URL didn't contain localhost address: %v", url) 20 | } 21 | } 22 | 23 | func TestHttpServerResponse(t *testing.T) { 24 | graph := NewGraph("fake title", GCVIS_TMPL) 25 | graph.AddGCTraceGraphPoint(&gctrace{}) 26 | server := NewHttpServer("127.0.0.1", "0", &graph) 27 | 28 | go server.Start() 29 | defer server.Close() 30 | 31 | response, err := http.Get(server.Url()) 32 | if err != nil { 33 | t.Errorf("HTTP request returned an error: %v", err) 34 | } 35 | defer response.Body.Close() 36 | 37 | body, err := ioutil.ReadAll(response.Body) 38 | if err != nil { 39 | t.Errorf("Error while reading response body: %v", err) 40 | } 41 | 42 | w := &bytes.Buffer{} 43 | 44 | if err = graph.Write(w); err != nil { 45 | t.Errorf("Error while writing template: %v", err) 46 | } 47 | 48 | expectedBody, err := ioutil.ReadAll(w) 49 | if err != nil { 50 | t.Errorf("Error while reading buffer: %v", err) 51 | } 52 | 53 | if !bytes.Equal(expectedBody, body) { 54 | t.Fatalf( 55 | "Expected response body to equal parsed template.\nExpected: %v\nGot: %v", 56 | string(expectedBody), 57 | string(body), 58 | ) 59 | } 60 | } 61 | 62 | func TestHttpServerJsonEndpoint(t *testing.T) { 63 | graph := NewGraph("fake title", GCVIS_TMPL) 64 | graph.AddGCTraceGraphPoint(&gctrace{Heap1: 10}) 65 | server := NewHttpServer("127.0.0.1", "0", &graph) 66 | 67 | go server.Start() 68 | defer server.Close() 69 | 70 | response, err := http.Get(server.Url() + "graph.json") 71 | if err != nil { 72 | t.Errorf("HTTP request returned an error: %v", err) 73 | } 74 | defer response.Body.Close() 75 | 76 | body, err := ioutil.ReadAll(response.Body) 77 | if err != nil { 78 | t.Errorf("Error while reading response body: %v", err) 79 | } 80 | 81 | result, err := json.Marshal(graph) 82 | if err != nil { 83 | t.Errorf("Error marshalling graph: %v", err) 84 | } 85 | 86 | if string(result) != strings.TrimRight(string(body), "\r\n") { 87 | t.Errorf("Expected graph to be a json string.\nExpected: %v\nGot: %v", string(result), string(body)) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | // gcvis is a tool to assist you visualising the operation of 2 | // the go runtime garbage collector. 3 | // 4 | // usage: 5 | // 6 | // gcvis program [arguments]... 7 | package main 8 | 9 | import ( 10 | "flag" 11 | "fmt" 12 | "io" 13 | "log" 14 | "os" 15 | "strings" 16 | 17 | "github.com/pkg/browser" 18 | 19 | "golang.org/x/crypto/ssh/terminal" 20 | ) 21 | 22 | var iface = flag.String("i", "127.0.0.1", "specify interface to use. defaults to 127.0.0.1.") 23 | var port = flag.String("p", "0", "specify port to use.") 24 | var openBrowser = flag.Bool("o", true, "automatically open browser") 25 | 26 | func main() { 27 | flag.Usage = func() { 28 | fmt.Fprintf(os.Stderr, "Usage of %s: command ...\n", os.Args[0]) 29 | flag.PrintDefaults() 30 | } 31 | 32 | var pipeRead io.ReadCloser 33 | var subcommand *SubCommand 34 | 35 | flag.Parse() 36 | if len(flag.Args()) < 1 { 37 | if terminal.IsTerminal(int(os.Stdin.Fd())) { 38 | flag.Usage() 39 | return 40 | } else { 41 | pipeRead = os.Stdin 42 | } 43 | } else { 44 | subcommand = NewSubCommand(flag.Args()) 45 | pipeRead = subcommand.PipeRead 46 | go subcommand.Run() 47 | } 48 | 49 | parser := NewParser(pipeRead) 50 | 51 | title := strings.Join(flag.Args(), " ") 52 | if len(title) == 0 { 53 | title = fmt.Sprintf("%s:%s", *iface, *port) 54 | } 55 | 56 | gcvisGraph := NewGraph(title, GCVIS_TMPL) 57 | server := NewHttpServer(*iface, *port, &gcvisGraph) 58 | 59 | go parser.Run() 60 | go server.Start() 61 | 62 | url := server.Url() 63 | 64 | if *openBrowser { 65 | log.Printf("opening browser window, if this fails, navigate to %s", url) 66 | browser.OpenURL(url) 67 | } else { 68 | log.Printf("server started on %s", url) 69 | } 70 | 71 | for { 72 | select { 73 | case gcTrace := <-parser.GcChan: 74 | gcvisGraph.AddGCTraceGraphPoint(gcTrace) 75 | case scvgTrace := <-parser.ScvgChan: 76 | gcvisGraph.AddScavengerGraphPoint(scvgTrace) 77 | case output := <-parser.NoMatchChan: 78 | fmt.Fprintln(os.Stderr, output) 79 | case <-parser.done: 80 | if parser.Err != nil { 81 | fmt.Fprintf(os.Stderr, parser.Err.Error()) 82 | os.Exit(1) 83 | } 84 | 85 | os.Exit(0) 86 | } 87 | } 88 | 89 | if subcommand != nil && subcommand.Err() != nil { 90 | fmt.Fprintf(os.Stderr, subcommand.Err().Error()) 91 | os.Exit(1) 92 | } 93 | 94 | os.Exit(0) 95 | } 96 | -------------------------------------------------------------------------------- /parser.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "io" 6 | "regexp" 7 | "strconv" 8 | ) 9 | 10 | const ( 11 | GCRegexpGo14 = `gc\d+\(\d+\): ([\d.]+\+?)+ us, \d+ -> (?P\d+) MB, \d+ \(\d+-\d+\) objects,( \d+ goroutines,)? \d+\/\d+\/\d+ sweeps, \d+\(\d+\) handoff, \d+\(\d+\) steal, \d+\/\d+\/\d+ yields` 12 | GCRegexpGo15 = `gc #?\d+ @(?P[\d.]+)s \d+%: [\d.+/]+ ms clock, [\d.+/]+ ms cpu, \d+->\d+->\d+ MB, (?P\d+) MB goal, \d+ P` 13 | GCRegexpGo16 = `gc #?\d+ @(?P[\d.]+)s \d+%: (?P[^+]+)\+(?P[^+]+)\+(?P[^+]+) ms clock, (?P[^+]+)\+(?P[^+]+)/(?P[^+]+)/(?P[^+]+)\+(?P[^+]+) ms cpu, \d+->\d+->\d+ MB, (?P\d+) MB goal, \d+ P` 14 | 15 | SCVGRegexp = `scvg\d+: inuse: (?P\d+), idle: (?P\d+), sys: (?P\d+), released: (?P\d+), consumed: (?P\d+) \(MB\)` 16 | ) 17 | 18 | var ( 19 | gcrego14 = regexp.MustCompile(GCRegexpGo14) 20 | gcrego15 = regexp.MustCompile(GCRegexpGo15) 21 | gcrego16 = regexp.MustCompile(GCRegexpGo16) 22 | scvgre = regexp.MustCompile(SCVGRegexp) 23 | ) 24 | 25 | type Parser struct { 26 | reader io.Reader 27 | GcChan chan *gctrace 28 | ScvgChan chan *scvgtrace 29 | NoMatchChan chan string 30 | done chan bool 31 | 32 | Err error 33 | 34 | scvgRegexp *regexp.Regexp 35 | } 36 | 37 | func NewParser(r io.Reader) *Parser { 38 | return &Parser{ 39 | reader: r, 40 | GcChan: make(chan *gctrace, 1), 41 | ScvgChan: make(chan *scvgtrace, 1), 42 | NoMatchChan: make(chan string, 1), 43 | done: make(chan bool), 44 | } 45 | } 46 | 47 | func (p *Parser) Run() { 48 | sc := bufio.NewScanner(p.reader) 49 | 50 | for sc.Scan() { 51 | line := sc.Text() 52 | if result := gcrego16.FindStringSubmatch(line); result != nil { 53 | p.GcChan <- parseGCTrace(gcrego16, result) 54 | continue 55 | } 56 | 57 | if result := gcrego15.FindStringSubmatch(line); result != nil { 58 | p.GcChan <- parseGCTrace(gcrego15, result) 59 | continue 60 | } 61 | 62 | if result := gcrego14.FindStringSubmatch(line); result != nil { 63 | p.GcChan <- parseGCTrace(gcrego14, result) 64 | continue 65 | } 66 | 67 | if result := scvgre.FindStringSubmatch(line); result != nil { 68 | p.ScvgChan <- parseSCVGTrace(result) 69 | continue 70 | } 71 | 72 | p.NoMatchChan <- line 73 | } 74 | 75 | p.Err = sc.Err() 76 | 77 | close(p.done) 78 | } 79 | 80 | func parseGCTrace(gcre *regexp.Regexp, matches []string) *gctrace { 81 | matchMap := getMatchMap(gcre, matches) 82 | 83 | return &gctrace{ 84 | Heap1: silentParseInt(matchMap["Heap1"]), 85 | ElapsedTime: silentParseFloat(matchMap["ElapsedTime"]), 86 | STWSclock: silentParseFloat(matchMap["STWSclock"]), 87 | MASclock: silentParseFloat(matchMap["MASclock"]), 88 | STWMclock: silentParseFloat(matchMap["STWMclock"]), 89 | STWScpu: silentParseFloat(matchMap["STWScpu"]), 90 | MASAssistcpu: silentParseFloat(matchMap["MASAssistcpu"]), 91 | MASBGcpu: silentParseFloat(matchMap["MASBGcpu"]), 92 | MASIdlecpu: silentParseFloat(matchMap["MASIdlecpu"]), 93 | STWMcpu: silentParseFloat(matchMap["STWMcpu"]), 94 | } 95 | } 96 | 97 | func parseSCVGTrace(matches []string) *scvgtrace { 98 | matchMap := getMatchMap(scvgre, matches) 99 | 100 | return &scvgtrace{ 101 | inuse: silentParseInt(matchMap["inuse"]), 102 | idle: silentParseInt(matchMap["idle"]), 103 | sys: silentParseInt(matchMap["sys"]), 104 | released: silentParseInt(matchMap["released"]), 105 | consumed: silentParseInt(matchMap["consumed"]), 106 | } 107 | } 108 | 109 | // Transform our matches in a readable hash map. 110 | // 111 | // The resulting hash map will be something like { "Heap1": 123 } 112 | func getMatchMap(re *regexp.Regexp, matches []string) map[string]string { 113 | matchingNames := re.SubexpNames()[1:] 114 | matchMap := map[string]string{} 115 | for i, value := range matches[1:] { 116 | if matchingNames[i] == "" { 117 | continue 118 | } 119 | matchMap[matchingNames[i]] = value 120 | } 121 | return matchMap 122 | } 123 | 124 | func silentParseInt(value string) int64 { 125 | intVal, err := strconv.ParseInt(value, 10, 64) 126 | if err != nil { 127 | return 0 128 | } 129 | 130 | return intVal 131 | } 132 | 133 | func silentParseFloat(value string) float64 { 134 | floatVal, err := strconv.ParseFloat(value, 64) 135 | if err != nil { 136 | return float64(0) 137 | } 138 | 139 | return float64(floatVal) 140 | } 141 | -------------------------------------------------------------------------------- /parser_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "reflect" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | var parser *Parser 11 | 12 | func runParserWith(line string) *Parser { 13 | reader := bytes.NewReader([]byte(line)) 14 | parser = NewParser(reader) 15 | go parser.Run() 16 | return parser 17 | } 18 | 19 | func TestParserWithMatchingInputGo16(t *testing.T) { 20 | line := "gc 763 @77536.239s 1%: 0.11+2192+0.75 ms clock, 0.92+9269/4379/3243+6.0 ms cpu, 6370->6390->3298 MB, 6533 MB goal, 8 P" 21 | 22 | runParserWith(line) 23 | 24 | expectedGCTrace := &gctrace{ 25 | Heap1: 6533, 26 | ElapsedTime: 77536.239, 27 | STWSclock: 0.11, 28 | MASclock: 2192, 29 | STWMclock: 0.75, 30 | STWScpu: 0.92, 31 | MASAssistcpu: 9269, 32 | MASBGcpu: 4379, 33 | MASIdlecpu: 3243, 34 | STWMcpu: 6.0, 35 | } 36 | 37 | select { 38 | case gctrace := <-parser.GcChan: 39 | if !reflect.DeepEqual(gctrace, expectedGCTrace) { 40 | t.Errorf("Expected gctrace to equal %+v. Got %+v instead.", expectedGCTrace, gctrace) 41 | } 42 | case <-time.After(100 * time.Millisecond): 43 | t.Fatalf("Execution timed out.") 44 | } 45 | } 46 | 47 | func TestParserWithMatchingInputGo15(t *testing.T) { 48 | line := "gc 88 @3.243s 9%: 0.040+16+1.0+5.9+0.34 ms clock, 0.16+16+0+18/5.7/11+1.3 ms cpu, 32->33->19 MB, 33 MB goal, 4 P" 49 | 50 | runParserWith(line) 51 | 52 | expectedGCTrace := &gctrace{ 53 | Heap1: 33, 54 | ElapsedTime: 3.243, 55 | } 56 | 57 | select { 58 | case gctrace := <-parser.GcChan: 59 | if !reflect.DeepEqual(gctrace, expectedGCTrace) { 60 | t.Errorf("Expected gctrace to equal %+v. Got %+v instead.", expectedGCTrace, gctrace) 61 | } 62 | case <-time.After(100 * time.Millisecond): 63 | t.Fatalf("Execution timed out.") 64 | } 65 | } 66 | 67 | func TestParserWithMatchingInputGo14(t *testing.T) { 68 | line := "gc76(1): 2+1+1390+1 us, 1 -> 3 MB, 16397 (1015746-999349) objects, 1436/1/0 sweeps, 0(0) handoff, 0(0) steal, 0/0/0 yields" 69 | 70 | runParserWith(line) 71 | 72 | expectedGCTrace := &gctrace{ 73 | Heap1: 3, 74 | } 75 | 76 | select { 77 | case gctrace := <-parser.GcChan: 78 | if !reflect.DeepEqual(gctrace, expectedGCTrace) { 79 | t.Errorf("Expected gctrace to equal %+v. Got %+v instead.", expectedGCTrace, gctrace) 80 | } 81 | case <-time.After(100 * time.Millisecond): 82 | t.Fatalf("Execution timed out.") 83 | } 84 | } 85 | 86 | func TestParserGoRoutinesInputGo14(t *testing.T) { 87 | line := "gc76(1): 2+1+1390+1 us, 1 -> 3 MB, 16397 (1015746-999349) objects, 12 goroutines, 1436/1/0 sweeps, 0(0) handoff, 0(0) steal, 0/0/0 yields" 88 | 89 | runParserWith(line) 90 | 91 | expectedGCTrace := &gctrace{ 92 | Heap1: 3, 93 | } 94 | 95 | select { 96 | case gctrace := <-parser.GcChan: 97 | if !reflect.DeepEqual(gctrace, expectedGCTrace) { 98 | t.Errorf("Expected gctrace to equal %+v. Got %+v instead.", expectedGCTrace, gctrace) 99 | } 100 | case <-time.After(100 * time.Millisecond): 101 | t.Fatalf("Execution timed out.") 102 | } 103 | } 104 | 105 | func TestParserWithScvgLine(t *testing.T) { 106 | line := "scvg1: inuse: 12, idle: 13, sys: 14, released: 15, consumed: 16 (MB)" 107 | 108 | runParserWith(line) 109 | 110 | expectedScvgTrace := &scvgtrace{ 111 | inuse: 12, 112 | idle: 13, 113 | sys: 14, 114 | released: 15, 115 | consumed: 16, 116 | } 117 | 118 | select { 119 | case scvgTrace := <-parser.ScvgChan: 120 | if !reflect.DeepEqual(scvgTrace, expectedScvgTrace) { 121 | t.Errorf("Expected scvgTrace to equal %+v. Got %+v instead.", expectedScvgTrace, scvgTrace) 122 | } 123 | case <-time.After(100 * time.Millisecond): 124 | t.Fatalf("Execution timed out.") 125 | } 126 | } 127 | 128 | func TestParserNonMatchingInput(t *testing.T) { 129 | line := "INFO: test" 130 | 131 | runParserWith(line) 132 | 133 | select { 134 | case <-parser.GcChan: 135 | t.Fatalf("Unexpected trace result. This input should not trigger gcChan.") 136 | case <-parser.ScvgChan: 137 | t.Fatalf("Unexpected trace result. This input should not trigger scvgChan.") 138 | case <-parser.NoMatchChan: 139 | return 140 | case <-time.After(100 * time.Millisecond): 141 | t.Fatalf("Execution timed out.") 142 | } 143 | } 144 | 145 | func TestParserWait(t *testing.T) { 146 | line := "INFO: wait" 147 | parser := runParserWith(line) 148 | 149 | select { 150 | case <-parser.done: 151 | return 152 | case <-time.After(100 * time.Millisecond): 153 | t.Fatalf("Execution timed out.") 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /subcommand.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io" 5 | "log" 6 | "os" 7 | "os/exec" 8 | "sync" 9 | ) 10 | 11 | type SubCommand struct { 12 | cmd *exec.Cmd 13 | PipeRead io.ReadCloser 14 | pipeWrite io.WriteCloser 15 | err error 16 | 17 | errMtx sync.Mutex 18 | } 19 | 20 | func NewSubCommand(args []string) *SubCommand { 21 | pipeRead, pipeWrite, err := os.Pipe() 22 | if err != nil { 23 | log.Fatal(err) 24 | } 25 | 26 | env := append(os.Environ(), "GODEBUG=gctrace=1") 27 | cmd := exec.Command(args[0], args[1:]...) 28 | cmd.Env = env 29 | cmd.Stdin = os.Stdin 30 | cmd.Stdout = os.Stdout 31 | cmd.Stderr = pipeWrite 32 | 33 | return &SubCommand{ 34 | cmd: cmd, 35 | PipeRead: pipeRead, 36 | pipeWrite: pipeWrite, 37 | } 38 | } 39 | 40 | func (s *SubCommand) Run() { 41 | s.setErr(s.cmd.Run()) 42 | s.pipeWrite.Close() 43 | } 44 | 45 | func (s *SubCommand) Err() error { 46 | s.errMtx.Lock() 47 | defer s.errMtx.Unlock() 48 | return s.err 49 | } 50 | 51 | func (s *SubCommand) setErr(err error) { 52 | s.errMtx.Lock() 53 | defer s.errMtx.Unlock() 54 | s.err = err 55 | } 56 | -------------------------------------------------------------------------------- /subcommand_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io/ioutil" 5 | "strings" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | func TestSubCommandPipeRead(t *testing.T) { 11 | cmd := []string{"/usr/bin/env", "bash", "-c", "echo hello world 1>&2"} 12 | subcommand := NewSubCommand(cmd) 13 | done := make(chan bool) 14 | 15 | go func() { 16 | subcommand.Run() 17 | 18 | content, err := ioutil.ReadAll(subcommand.PipeRead) 19 | if err != nil { 20 | t.Fatalf("ReadAll returned an error: %v", err) 21 | } 22 | 23 | if strings.TrimRight(string(content), "\r\n ") != "hello world" { 24 | t.Errorf("line is not equal to 'hello world': '%v'", string(content)) 25 | } 26 | 27 | close(done) 28 | }() 29 | 30 | select { 31 | case <-done: 32 | return 33 | case <-time.After(100 * time.Millisecond): 34 | t.Fatalf("Execution timed out.") 35 | } 36 | } 37 | 38 | func TestSubCommandFail(t *testing.T) { 39 | cmd := []string{"/usr/bin/env", "bash", "-c", "echo hello world 1>&2; exit 1"} 40 | subcommand := NewSubCommand(cmd) 41 | done := make(chan bool) 42 | 43 | go func() { 44 | subcommand.Run() 45 | 46 | content, err := ioutil.ReadAll(subcommand.PipeRead) 47 | if err != nil { 48 | t.Fatalf("ReadAll returned an error: %v", err) 49 | } 50 | 51 | if strings.TrimRight(string(content), "\r\n ") != "hello world" { 52 | t.Errorf("line is not equal to 'hello world': '%v'", string(content)) 53 | } 54 | 55 | close(done) 56 | }() 57 | 58 | select { 59 | case <-done: 60 | if subcommand.Err() == nil { 61 | t.Errorf("Expected subcommand to have an error assigned.") 62 | } 63 | return 64 | case <-time.After(100 * time.Millisecond): 65 | t.Fatalf("Execution timed out.") 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /template.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | const ( 4 | GCVIS_TMPL = ` 5 | 6 | 7 | gcvis - {{ .Title }} 8 | 9 | 10 | 11 | 12 | 13 | 220 | 300 | 301 | 302 |
{{ .Title }}
303 |
304 | json 305 |
306 |
307 | 308 |
309 |
310 |
311 | 312 |
313 |
314 |
315 | 316 |
317 |
318 |
319 | 320 |
321 |
322 |
323 | 324 |

The smaller plot is linked to the main plot, so it acts as an overview. Try dragging a selection on either plot, and watch the behavior of the other.

325 | 326 |
327 | 328 |
Legend
329 | 
330 | 331 |
gc.heapinuse
heap in use after gc
332 |
scvg.inuse
virtual memory considered in use by the scavenger
333 |
scvg.idle
virtual memory considered unused by the scavenger
334 |
scvg.sys
virtual memory requested from the operating system (should aproximate VSS)
335 |
scvg.released
virtual memory returned to the operating system by the scavenger
336 |
scvg.consumed
virtual memory in use (should roughly match process RSS)
337 | 338 |
STW sweep clock
stop-the-world sweep clock time
339 |
con mas clock
concurrent mark and scan clock time
340 |
STW mark clock
stop-the-world mark clock time
341 |
STW sweep cpu
stop-the-world sweep cpu time
342 |
con mas assist cpu
concurrent mark and scan - assist cpu time (GC performed in line with allocation)
343 |
con mas bg cpu
concurrent mark and scan - background GC cpu time
344 |
con mas idle cpu
concurrent mark and scan - idle GC cpu time
345 |
STW mark cpu
stop-the-world mark cpu time
346 |
347 | 348 |
349 | 350 | 351 | ` 352 | ) 353 | -------------------------------------------------------------------------------- /tracedata.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type scvgtrace struct { 4 | ElapsedTime float64 // in seconds 5 | inuse int64 6 | idle int64 7 | sys int64 8 | released int64 9 | consumed int64 10 | } 11 | 12 | type gctrace struct { 13 | ElapsedTime float64 // in seconds 14 | NumGC int64 15 | Nproc int64 16 | t1 int64 17 | t2 int64 18 | t3 int64 19 | t4 int64 20 | Heap0 int64 // heap size before, in megabytes 21 | Heap1 int64 // heap size after, in megabytes 22 | Obj int64 23 | NMalloc int64 24 | NFree int64 25 | NSpan int64 26 | NGoRoutines int64 27 | NBGSweep int64 28 | NPauseSweep int64 29 | NHandoff int64 30 | NHandoffCnt int64 31 | NSteal int64 32 | NStealCnt int64 33 | NProcYield int64 34 | NOsYield int64 35 | NSleep int64 36 | STWSclock float64 37 | MASclock float64 38 | STWMclock float64 39 | STWScpu float64 40 | MASAssistcpu float64 41 | MASBGcpu float64 42 | MASIdlecpu float64 43 | STWMcpu float64 44 | } 45 | --------------------------------------------------------------------------------