ruby-prof 0.18.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (112) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES +23 -0
  3. data/LICENSE +2 -2
  4. data/README.rdoc +1 -483
  5. data/Rakefile +3 -6
  6. data/bin/ruby-prof +65 -30
  7. data/ext/ruby_prof/extconf.rb +6 -38
  8. data/ext/ruby_prof/rp_allocation.c +292 -0
  9. data/ext/ruby_prof/rp_allocation.h +31 -0
  10. data/ext/ruby_prof/rp_call_info.c +137 -279
  11. data/ext/ruby_prof/rp_call_info.h +16 -34
  12. data/ext/ruby_prof/rp_measure_allocations.c +25 -49
  13. data/ext/ruby_prof/rp_measure_memory.c +21 -56
  14. data/ext/ruby_prof/rp_measure_process_time.c +28 -36
  15. data/ext/ruby_prof/rp_measure_wall_time.c +36 -19
  16. data/ext/ruby_prof/rp_measurement.c +236 -0
  17. data/ext/ruby_prof/rp_measurement.h +49 -0
  18. data/ext/ruby_prof/rp_method.c +395 -383
  19. data/ext/ruby_prof/rp_method.h +34 -39
  20. data/ext/ruby_prof/rp_profile.c +881 -0
  21. data/ext/ruby_prof/rp_profile.h +36 -0
  22. data/ext/ruby_prof/rp_stack.c +103 -80
  23. data/ext/ruby_prof/rp_stack.h +5 -12
  24. data/ext/ruby_prof/rp_thread.c +149 -88
  25. data/ext/ruby_prof/rp_thread.h +15 -6
  26. data/ext/ruby_prof/ruby_prof.c +11 -757
  27. data/ext/ruby_prof/ruby_prof.h +4 -47
  28. data/ext/ruby_prof/vc/ruby_prof.vcxproj +10 -8
  29. data/lib/ruby-prof.rb +2 -17
  30. data/lib/ruby-prof/assets/graph_printer.html.erb +356 -0
  31. data/lib/ruby-prof/call_info.rb +35 -93
  32. data/lib/ruby-prof/call_info_visitor.rb +19 -21
  33. data/lib/ruby-prof/compatibility.rb +37 -107
  34. data/lib/ruby-prof/exclude_common_methods.rb +198 -0
  35. data/lib/ruby-prof/measurement.rb +14 -0
  36. data/lib/ruby-prof/method_info.rb +52 -83
  37. data/lib/ruby-prof/printers/abstract_printer.rb +66 -52
  38. data/lib/ruby-prof/printers/call_info_printer.rb +13 -3
  39. data/lib/ruby-prof/printers/call_stack_printer.rb +32 -28
  40. data/lib/ruby-prof/printers/call_tree_printer.rb +20 -12
  41. data/lib/ruby-prof/printers/dot_printer.rb +5 -5
  42. data/lib/ruby-prof/printers/flat_printer.rb +6 -24
  43. data/lib/ruby-prof/printers/graph_html_printer.rb +7 -192
  44. data/lib/ruby-prof/printers/graph_printer.rb +13 -15
  45. data/lib/ruby-prof/printers/multi_printer.rb +66 -23
  46. data/lib/ruby-prof/profile.rb +10 -3
  47. data/lib/ruby-prof/rack.rb +0 -3
  48. data/lib/ruby-prof/thread.rb +12 -12
  49. data/lib/ruby-prof/version.rb +1 -1
  50. data/ruby-prof.gemspec +2 -2
  51. data/test/abstract_printer_test.rb +0 -27
  52. data/test/alias_test.rb +129 -0
  53. data/test/basic_test.rb +41 -40
  54. data/test/call_info_visitor_test.rb +3 -3
  55. data/test/dynamic_method_test.rb +0 -2
  56. data/test/line_number_test.rb +120 -39
  57. data/test/marshal_test.rb +119 -0
  58. data/test/measure_allocations.rb +30 -0
  59. data/test/measure_allocations_test.rb +371 -12
  60. data/test/measure_allocations_trace_test.rb +385 -0
  61. data/test/measure_memory_trace_test.rb +756 -0
  62. data/test/measure_process_time_test.rb +821 -33
  63. data/test/measure_times.rb +54 -0
  64. data/test/measure_wall_time_test.rb +349 -145
  65. data/test/multi_printer_test.rb +1 -34
  66. data/test/parser_timings.rb +24 -0
  67. data/test/pause_resume_test.rb +5 -5
  68. data/test/prime.rb +2 -0
  69. data/test/printer_call_tree_test.rb +31 -0
  70. data/test/printer_flat_test.rb +68 -0
  71. data/test/printer_graph_html_test.rb +60 -0
  72. data/test/printer_graph_test.rb +41 -0
  73. data/test/printers_test.rb +32 -166
  74. data/test/printing_recursive_graph_test.rb +26 -72
  75. data/test/recursive_test.rb +72 -77
  76. data/test/stack_printer_test.rb +2 -15
  77. data/test/start_stop_test.rb +22 -25
  78. data/test/test_helper.rb +5 -248
  79. data/test/thread_test.rb +11 -54
  80. data/test/unique_call_path_test.rb +16 -28
  81. data/test/yarv_test.rb +1 -0
  82. metadata +24 -34
  83. data/examples/flat.txt +0 -50
  84. data/examples/graph.dot +0 -84
  85. data/examples/graph.html +0 -823
  86. data/examples/graph.txt +0 -139
  87. data/examples/multi.flat.txt +0 -23
  88. data/examples/multi.graph.html +0 -760
  89. data/examples/multi.grind.dat +0 -114
  90. data/examples/multi.stack.html +0 -547
  91. data/examples/stack.html +0 -547
  92. data/ext/ruby_prof/rp_measure.c +0 -40
  93. data/ext/ruby_prof/rp_measure.h +0 -45
  94. data/ext/ruby_prof/rp_measure_cpu_time.c +0 -136
  95. data/ext/ruby_prof/rp_measure_gc_runs.c +0 -73
  96. data/ext/ruby_prof/rp_measure_gc_time.c +0 -60
  97. data/lib/ruby-prof/aggregate_call_info.rb +0 -76
  98. data/lib/ruby-prof/printers/flat_printer_with_line_numbers.rb +0 -83
  99. data/lib/ruby-prof/profile/exclude_common_methods.rb +0 -207
  100. data/lib/ruby-prof/profile/legacy_method_elimination.rb +0 -50
  101. data/test/aggregate_test.rb +0 -136
  102. data/test/block_test.rb +0 -74
  103. data/test/call_info_test.rb +0 -78
  104. data/test/fiber_test.rb +0 -79
  105. data/test/issue137_test.rb +0 -63
  106. data/test/measure_cpu_time_test.rb +0 -212
  107. data/test/measure_gc_runs_test.rb +0 -32
  108. data/test/measure_gc_time_test.rb +0 -36
  109. data/test/measure_memory_test.rb +0 -33
  110. data/test/method_elimination_test.rb +0 -84
  111. data/test/module_test.rb +0 -45
  112. data/test/stack_test.rb +0 -138
@@ -1,40 +0,0 @@
1
- /* Copyright (C) 2005-2019 Shugo Maeda <shugo@ruby-lang.org> and Charlie Savage <cfis@savagexi.com>
2
- Please see the LICENSE file for copyright and distribution information */
3
-
4
- #include "ruby_prof.h"
5
-
6
- VALUE mMeasure;
7
-
8
- prof_measurer_t* prof_get_measurer(prof_measure_mode_t measure)
9
- {
10
- switch (measure) {
11
- case MEASURE_WALL_TIME:
12
- return prof_measurer_wall_time();
13
- case MEASURE_PROCESS_TIME:
14
- return prof_measurer_process_time();
15
- case MEASURE_CPU_TIME:
16
- return prof_measurer_cpu_time();
17
- case MEASURE_ALLOCATIONS:
18
- return prof_measurer_allocations();
19
- case MEASURE_MEMORY:
20
- return prof_measurer_memory();
21
- case MEASURE_GC_TIME:
22
- return prof_measurer_gc_time();
23
- case MEASURE_GC_RUNS:
24
- return prof_measurer_gc_runs();
25
- default:
26
- rb_raise(rb_eArgError, "Unknown measure mode: %d", measure);
27
- }
28
- };
29
-
30
- void rp_init_measure()
31
- {
32
- mMeasure = rb_define_module_under(mProf, "Measure");
33
- rp_init_measure_wall_time();
34
- rp_init_measure_cpu_time();
35
- rp_init_measure_process_time();
36
- rp_init_measure_allocations();
37
- rp_init_measure_memory();
38
- rp_init_measure_gc_time();
39
- rp_init_measure_gc_runs();
40
- }
@@ -1,45 +0,0 @@
1
- /* Copyright (C) 2005-2019 Shugo Maeda <shugo@ruby-lang.org> and Charlie Savage <cfis@savagexi.com>
2
- Please see the LICENSE file for copyright and distribution information */
3
-
4
- #ifndef __RP_MEASUREMENT_H__
5
- #define __RP_MEASUREMENT_H__
6
-
7
- extern VALUE mMeasure;
8
-
9
- typedef double (*get_measurement)();
10
-
11
- typedef struct
12
- {
13
- get_measurement measure;
14
- } prof_measurer_t;
15
-
16
- typedef enum
17
- {
18
- MEASURE_WALL_TIME,
19
- MEASURE_PROCESS_TIME,
20
- MEASURE_CPU_TIME,
21
- MEASURE_ALLOCATIONS,
22
- MEASURE_MEMORY,
23
- MEASURE_GC_TIME,
24
- MEASURE_GC_RUNS,
25
- } prof_measure_mode_t;
26
-
27
- prof_measurer_t* prof_get_measurer(prof_measure_mode_t measure);
28
- prof_measurer_t* prof_measurer_allocations();
29
- prof_measurer_t* prof_measurer_cpu_time();
30
- prof_measurer_t* prof_measurer_gc_runs();
31
- prof_measurer_t* prof_measurer_gc_time();
32
- prof_measurer_t* prof_measurer_memory();
33
- prof_measurer_t* prof_measurer_process_time();
34
- prof_measurer_t* prof_measurer_wall_time();
35
-
36
- void rp_init_measure();
37
- void rp_init_measure_allocations();
38
- void rp_init_measure_cpu_time();
39
- void rp_init_measure_gc_runs();
40
- void rp_init_measure_gc_time();
41
- void rp_init_measure_memory();
42
- void rp_init_measure_process_time();
43
- void rp_init_measure_wall_time();
44
-
45
- #endif //__RP_MEASUREMENT_H__
@@ -1,136 +0,0 @@
1
- /* Copyright (C) 2005-2013 Shugo Maeda <shugo@ruby-lang.org> and Charlie Savage <cfis@savagexi.com>
2
- Please see the LICENSE file for copyright and distribution information */
3
-
4
- #include "ruby_prof.h"
5
-
6
- static VALUE cMeasureCpuTime;
7
-
8
- /* The _WIN32 check is needed for msys (and maybe cygwin?) */
9
- #if defined(_WIN32)
10
-
11
- static unsigned long long get_cpu_time()
12
- {
13
- LARGE_INTEGER time;
14
- QueryPerformanceCounter(&time);
15
- return time.QuadPart;
16
- }
17
-
18
- static unsigned long long get_cpu_frequency()
19
- {
20
- static unsigned long long cpu_frequency;
21
-
22
- if(!cpu_frequency) {
23
- LARGE_INTEGER cpu_frequency_struct;
24
- QueryPerformanceFrequency(&cpu_frequency_struct);
25
- cpu_frequency = cpu_frequency_struct.QuadPart;
26
- }
27
-
28
- return cpu_frequency;
29
- }
30
-
31
- static double measure_cpu_time()
32
- {
33
- return ((double)get_cpu_time()) / get_cpu_frequency();
34
- }
35
-
36
- #else
37
-
38
- #include <sys/resource.h>
39
- #include <stdint.h>
40
- #include <time.h>
41
-
42
- static unsigned long long get_cpu_time()
43
- {
44
- #if defined(__i386__) || defined(__x86_64__)
45
- uint32_t a, d;
46
- __asm__ volatile("rdtsc" : "=a" (a), "=d" (d));
47
- return ((uint64_t)d << 32) + a;
48
- #elif defined(__powerpc__) || defined(__ppc__)
49
- unsigned long long x, y;
50
-
51
- __asm__ __volatile__ ("\n\
52
- 1: mftbu %1\n\
53
- mftb %L0\n\
54
- mftbu %0\n\
55
- cmpw %0,%1\n\
56
- bne- 1b"
57
- : "=r" (x), "=r" (y));
58
-
59
- return x;
60
- #endif
61
- }
62
-
63
- static unsigned long long get_cpu_frequency()
64
- {
65
- static unsigned long long cpu_frequency;
66
-
67
- if(!cpu_frequency) {
68
- unsigned long long x, y;
69
-
70
- struct timespec ts;
71
- ts.tv_sec = 0;
72
- ts.tv_nsec = 500000000;
73
- x = get_cpu_time();
74
- nanosleep(&ts, NULL);
75
- y = get_cpu_time();
76
- cpu_frequency = (y - x) * 2;
77
- }
78
-
79
- return cpu_frequency;
80
- }
81
-
82
- static double measure_cpu_time()
83
- {
84
- struct rusage rusage;
85
- getrusage(RUSAGE_SELF, &rusage);
86
-
87
- double seconds = 0;
88
-
89
- seconds += rusage.ru_utime.tv_sec;
90
- seconds += rusage.ru_stime.tv_sec;
91
-
92
- seconds += rusage.ru_utime.tv_usec / 1000000.0;
93
- seconds += rusage.ru_stime.tv_usec / 1000000.0;
94
-
95
- return seconds;
96
- }
97
- #endif
98
-
99
-
100
- prof_measurer_t* prof_measurer_cpu_time()
101
- {
102
- prof_measurer_t* measure = ALLOC(prof_measurer_t);
103
- measure->measure = measure_cpu_time;
104
- return measure;
105
- }
106
-
107
- /* call-seq:
108
- measure -> float
109
-
110
- Returns the cpu time.*/
111
- static VALUE
112
- prof_measure_cpu_time(VALUE self)
113
- {
114
- return rb_float_new(measure_cpu_time());
115
- }
116
-
117
- /* call-seq:
118
- cpu_frequency -> int
119
-
120
- Returns the cpu's frequency. This value is needed when
121
- RubyProf::measure_mode is set to CPU_TIME. */
122
- static VALUE
123
- prof_get_cpu_frequency(VALUE self)
124
- {
125
- return ULL2NUM(get_cpu_frequency());
126
- }
127
-
128
- void rp_init_measure_cpu_time()
129
- {
130
- rb_define_const(mProf, "CPU_TIME", INT2NUM(MEASURE_CPU_TIME));
131
- rb_define_const(mProf, "CPU_TIME_ENABLED", Qtrue);
132
-
133
- cMeasureCpuTime = rb_define_class_under(mMeasure, "CpuTime", rb_cObject);
134
- rb_define_singleton_method(cMeasureCpuTime, "measure", prof_measure_cpu_time, 0);
135
- rb_define_singleton_method(cMeasureCpuTime, "frequency", prof_get_cpu_frequency, 0);
136
- }
@@ -1,73 +0,0 @@
1
- /* Copyright (C) 2005-2013 Shugo Maeda <shugo@ruby-lang.org> and Charlie Savage <cfis@savagexi.com>
2
- Please see the LICENSE file for copyright and distribution information */
3
-
4
- /* :nodoc: */
5
-
6
- #include "ruby_prof.h"
7
-
8
- static VALUE cMeasureGcRuns;
9
-
10
- #if defined(HAVE_RB_GC_COLLECTIONS)
11
- VALUE rb_gc_collections(void);
12
- #endif
13
-
14
- #if defined(HAVE_RB_GC_COUNT)
15
- size_t rb_gc_count(void);
16
- #endif
17
-
18
- #if defined(HAVE_RB_GC_HEAP_INFO)
19
- VALUE rb_gc_heap_info(void);
20
- #endif
21
-
22
-
23
- static double
24
- measure_gc_runs()
25
- {
26
- #if defined(HAVE_RB_GC_COLLECTIONS)
27
- #define MEASURE_GC_RUNS_ENABLED Qtrue
28
- return NUM2INT(rb_gc_collections());
29
-
30
- #elif defined(HAVE_RB_GC_COUNT)
31
- #define MEASURE_GC_RUNS_ENABLED Qtrue
32
- return rb_gc_count();
33
-
34
- #elif defined(HAVE_RB_GC_HEAP_INFO)
35
- #define MEASURE_GC_RUNS_ENABLED Qtrue
36
- VALUE h = rb_gc_heap_info();
37
- return NUM2UINT(rb_hash_aref(h, rb_str_new2("num_gc_passes")));
38
-
39
- #else
40
- #define MEASURE_GC_RUNS_ENABLED Qfalse
41
- return 0;
42
- #endif
43
- }
44
-
45
- prof_measurer_t* prof_measurer_gc_runs()
46
- {
47
- prof_measurer_t* measure = ALLOC(prof_measurer_t);
48
- measure->measure = measure_gc_runs;
49
- return measure;
50
- }
51
-
52
- /* call-seq:
53
- measure -> int
54
-
55
- Returns the number of GC runs.*/
56
- static VALUE
57
- prof_measure_gc_runs(VALUE self)
58
- {
59
- #if defined(HAVE_LONG_LONG)
60
- return ULL2NUM(measure_gc_runs());
61
- #else
62
- return ULONG2NUM(measure_gc_runs());
63
- #endif
64
- }
65
-
66
- void rp_init_measure_gc_runs()
67
- {
68
- rb_define_const(mProf, "GC_RUNS", INT2NUM(MEASURE_GC_RUNS));
69
- rb_define_const(mProf, "GC_RUNS_ENABLED", MEASURE_GC_RUNS_ENABLED);
70
-
71
- cMeasureGcRuns = rb_define_class_under(mMeasure, "GcRuns", rb_cObject);
72
- rb_define_singleton_method(cMeasureGcRuns, "measure", prof_measure_gc_runs, 0);
73
- }
@@ -1,60 +0,0 @@
1
- /* Copyright (C) 2005-2019 Shugo Maeda <shugo@ruby-lang.org> and Charlie Savage <cfis@savagexi.com>
2
- Please see the LICENSE file for copyright and distribution information */
3
-
4
- /* :nodoc: */
5
-
6
- #include "ruby_prof.h"
7
-
8
- static VALUE cMeasureGcTimes;
9
-
10
- #if defined(HAVE_RB_GC_TIME)
11
- VALUE rb_gc_time();
12
- #endif
13
-
14
- static double
15
- measure_gc_time()
16
- {
17
- #if defined(HAVE_RB_GC_TOTAL_TIME)
18
- #define MEASURE_GC_TIME_ENABLED Qtrue
19
- return rb_gc_total_time();
20
-
21
- #elif defined(HAVE_RB_GC_TIME)
22
- #define MEASURE_GC_TIME_ENABLED Qtrue
23
- const double conversion = 1000000.0;
24
- #if HAVE_LONG_LONG
25
- return NUM2LL(rb_gc_time()) / conversion;
26
- #else
27
- return NUM2LONG(rb_gc_time()) / conversion;
28
- #endif
29
-
30
- #else
31
- #define MEASURE_GC_TIME_ENABLED Qfalse
32
- return 0.0;
33
- #endif
34
- }
35
-
36
- prof_measurer_t* prof_measurer_gc_time()
37
- {
38
- prof_measurer_t* measure = ALLOC(prof_measurer_t);
39
- measure->measure = measure_gc_time;
40
- return measure;
41
- }
42
-
43
- /* call-seq:
44
- measure -> float
45
-
46
- Returns the time spent performing GC.*/
47
- static VALUE
48
- prof_measure_gc_time(VALUE self)
49
- {
50
- return rb_float_new(measure_gc_time());
51
- }
52
-
53
- void rp_init_measure_gc_time()
54
- {
55
- rb_define_const(mProf, "GC_TIME", INT2NUM(MEASURE_GC_TIME));
56
- rb_define_const(mProf, "GC_TIME_ENABLED", MEASURE_GC_TIME_ENABLED);
57
-
58
- cMeasureGcTimes = rb_define_class_under(mMeasure, "GcTime", rb_cObject);
59
- rb_define_singleton_method(cMeasureGcTimes, "measure", prof_measure_gc_time, 0);
60
- }
@@ -1,76 +0,0 @@
1
- # encoding: utf-8
2
-
3
- module RubyProf
4
- class AggregateCallInfo
5
- attr_reader :call_infos, :method_info
6
-
7
- def initialize(call_infos, method_info)
8
- if call_infos.length == 0
9
- raise(ArgumentError, "Must specify at least one call info.")
10
- end
11
- @call_infos = call_infos
12
- @method_info = method_info
13
- end
14
-
15
- def target
16
- call_infos.first.target
17
- end
18
-
19
- def parent
20
- call_infos.first.parent
21
- end
22
-
23
- def line
24
- call_infos.first.line
25
- end
26
-
27
- def children
28
- call_infos.inject(Array.new) do |result, call_info|
29
- result.concat(call_info.children)
30
- end
31
- end
32
-
33
- def total_time
34
- aggregate_roots(:total_time)
35
- end
36
-
37
- def self_time
38
- aggregate_roots(:self_time)
39
- end
40
-
41
- def wait_time
42
- aggregate_roots(:wait_time)
43
- end
44
-
45
- def children_time
46
- aggregate_roots(:children_time)
47
- end
48
-
49
- def called
50
- aggregate_all(:called)
51
- end
52
-
53
- def to_s
54
- "#{call_infos.first.target.full_name}"
55
- end
56
-
57
- private
58
-
59
- # return all call_infos which are not (grand) children of any other node in the list of given call_infos
60
- def roots
61
- @roots ||= method_info.recursive? ? CallInfo.roots_of(call_infos) : call_infos
62
- end
63
-
64
- def aggregate_all(method_name)
65
- call_infos.inject(0) do |sum, call_info|
66
- sum + call_info.send(method_name)
67
- end
68
- end
69
-
70
- def aggregate_roots(method_name)
71
- roots.inject(0) do |sum, call_info|
72
- sum + call_info.send(method_name)
73
- end
74
- end
75
- end
76
- end
@@ -1,83 +0,0 @@
1
- # encoding: utf-8
2
-
3
- module RubyProf
4
- # Generates flat[link:files/examples/flat_txt.html] profile reports as text.
5
- # To use the flat printer with line numbers:
6
- #
7
- # result = RubyProf.profile do
8
- # [code to profile]
9
- # end
10
- #
11
- # printer = RubyProf::FlatPrinterWithLineNumbers.new(result)
12
- # printer.print(STDOUT, {})
13
- #
14
- class FlatPrinterWithLineNumbers < FlatPrinter
15
- def print_methods(thread)
16
- @editor = editor_uri
17
- total_time = thread.total_time
18
-
19
- methods = thread.methods.sort_by(&sort_method).reverse
20
- sum = 0
21
- methods.each do |method|
22
- self_percent = (method.self_time / total_time) * 100
23
- next if self_percent < min_percent
24
-
25
- sum += method.self_time
26
- #self_time_called = method.called > 0 ? method.self_time/method.called : 0
27
- #total_time_called = method.called > 0? method.total_time/method.called : 0
28
-
29
- @output << "%6.2f %9.3f %9.3f %9.3f %9.3f %8d %s%s" % [
30
- method.self_time / total_time * 100, # %self
31
- method.total_time, # total
32
- method.self_time, # self
33
- method.wait_time, # wait
34
- method.children_time, # children
35
- method.called, # calls
36
- method.recursive? ? "*" : " ", # cycle
37
- method_name(method) # name
38
- ]
39
- if method.source_file == 'ruby_runtime'
40
- @output << "\n"
41
- else
42
- @output << "\n defined at:\n"
43
- @output << defined_at_format % [File.expand_path(method.source_file), method.line]
44
- end
45
-
46
- callers = []
47
- method.call_infos.each do |ci|
48
- if ci.parent && ci.parent.target.source_file != 'ruby_runtime'
49
- callers << [method_name(ci.parent.target), File.expand_path(ci.parent.target.source_file), ci.parent.target.line]
50
- end
51
- end
52
- # make sure callers are unique
53
- callers.uniq!
54
-
55
- unless callers.empty?
56
- @output << " called from:\n"
57
- callers.each do |args|
58
- @output << called_from_format % args
59
- end
60
- end
61
- @output << "\n"
62
- end
63
- end
64
-
65
- private
66
-
67
- def defined_at_format
68
- if @editor
69
- " #{@editor}://open?url=file://%s&line=%s\n"
70
- else
71
- " %s:%s\n"
72
- end
73
- end
74
-
75
- def called_from_format
76
- if @editor
77
- " %s (#{@editor}://open?url=file://%s&line=%s)\n"
78
- else
79
- " %s (%s:%s)\n"
80
- end
81
- end
82
- end
83
- end