1 module tagion.tools.tprofview; 2 3 import std.algorithm; 4 import std.conv : to; 5 import std.exception : assumeWontThrow; 6 import std.file : exists; 7 import std.format; 8 import std.getopt; 9 import std.range; 10 import std.stdio; 11 import std.traits; 12 import std.typecons; 13 import std.utf; 14 import tagion.tools.Basic; 15 import tagion.tools.revision; 16 17 mixin Main!(_main); 18 19 struct ProfLine { 20 uint num_calls; 21 uint tree_time; 22 uint func_time; 23 double per_calls; 24 string func_name; 25 this(const uint num_calls, const uint tree_time, const uint func_time, string func_name) pure @nogc { 26 this.num_calls = num_calls; 27 this.tree_time = tree_time; 28 this.func_time = func_time; 29 per_calls = double(tree_time) / double(num_calls); 30 this.func_name = func_name; 31 } 32 33 string toString() const { 34 return format("%8d %8d %8d %8.3f %s", num_calls, tree_time, func_time, 35 per_calls, 36 func_name); 37 } 38 39 } 40 41 enum ProfSort { 42 calls, 43 tree, 44 func, 45 percalls, 46 } 47 48 struct ProfInfo { 49 string[] head; 50 ProfLine[] proflines; 51 void display(ProfSort sort_option)(const uint num) const { 52 scope list_sort = proflines.dup 53 .sort!((a, b) => a.tupleof[sort_option] > b.tupleof[sort_option]); 54 head.each!writeln; 55 list_sort 56 .take(num) 57 .each!writeln; 58 } 59 60 void display(const ProfSort sort_option, const uint num) const { 61 SortOption: 62 final switch (sort_option) { 63 static foreach (E; EnumMembers!ProfSort) { 64 case E: 65 display!E(num); 66 break SortOption; 67 } 68 } 69 } 70 } 71 72 ProfInfo loadProf(ref File fin, const bool verbose) { 73 ProfInfo result; 74 bool validUTF(Line)(Line line) nothrow { 75 try { 76 line.value.validate; 77 } 78 catch (Exception e) { 79 if (verbose) { 80 assumeWontThrow({ writeln(e.msg); writefln("%d:%s", line.index, line.value); }()); 81 } 82 return false; 83 } 84 return true; 85 } 86 87 auto prof_file = fin.byLine // .map!(l => l.filter!(c => c.isValidDchar)) 88 .enumerate(1) 89 .filter!(l => validUTF(l)) 90 .map!(l => l.value.toUTF8); 91 prof_file 92 .find!(l => l.startsWith("========")); //, "==")); //, "======")); 93 result.head = prof_file.take(4).map!(l => l.idup).array; 94 result.proflines = prof_file 95 .map!(l => tuple( 96 l.split.take(3).map!(n => n.to!uint).array, 97 l.split.drop(4).join(" "))) 98 .filter!(prof => prof[1].length > 0) 99 .map!(prof => ProfLine(prof[0][0], prof[0][1], prof[0][2], prof[1].idup)) 100 .array; 101 return result; 102 } 103 104 int _main(string[] args) { 105 immutable program = args[0]; 106 107 bool version_switch; 108 uint number_tobe_listed = 10; 109 string trace_file = "trace.log"; 110 ProfSort prof_sort = ProfSort.tree; 111 bool verbose_switch; 112 auto main_args = getopt(args, 113 std.getopt.config.caseSensitive, 114 std.getopt.config.bundling, 115 "version", "display the version", &version_switch, 116 "l", format("Number to be listed (default %d)", number_tobe_listed), &number_tobe_listed, 117 "s", format("Sort flag %s (default %s)", [EnumMembers!ProfSort], prof_sort), &prof_sort, 118 "v|verbose", "verbose switch", &verbose_switch, 119 ); 120 121 if (version_switch) { 122 revision_text.writeln; 123 return 0; 124 } 125 126 if (main_args.helpWanted) { 127 defaultGetoptPrinter( 128 [ 129 revision_text, 130 "Documentation: https://tagion.org/", 131 "", 132 "Usage:", 133 format("%s [<option>...] <trace-file> ", program), 134 "", 135 "<option>:", 136 137 ].join("\n"), 138 main_args.options); 139 return 0; 140 } 141 if (args.length > 1) { 142 trace_file = args[1]; 143 writefln("trace file %s", trace_file); 144 } 145 try { 146 auto fin = File(trace_file, "r"); 147 scope (exit) { 148 fin.close; 149 } 150 const prof_list = loadProf(fin, verbose_switch); 151 prof_list.display(prof_sort, number_tobe_listed); 152 } 153 catch (Exception e) { 154 stderr.writeln(e.msg); 155 return 1; 156 } 157 return 0; 158 }