lumitrace 0.4.2 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,6 +7,8 @@ module Lumitrace
7
7
  end
8
8
 
9
9
  module RecordInstrument
10
+ IDENTIFIER_METHOD_NAME_RE = /\A[a-z_]\w*[!?=]?\z/.freeze
11
+
10
12
  SKIP_NODE_CLASSES = [
11
13
  Prism::DefNode,
12
14
  Prism::ClassNode,
@@ -99,7 +101,7 @@ module RecordInstrument
99
101
  end
100
102
  end
101
103
 
102
- node.child_nodes.each { |child| stack << [child, node] }
104
+ instrumentable_child_nodes(node).each { |child| stack << [child, node] }
103
105
  end
104
106
  locs
105
107
  end
@@ -136,7 +138,7 @@ module RecordInstrument
136
138
  end
137
139
  end
138
140
 
139
- node.child_nodes.each { |child| stack << [child, node] }
141
+ instrumentable_child_nodes(node).each { |child| stack << [child, node] }
140
142
  end
141
143
 
142
144
  inserts
@@ -177,6 +179,8 @@ module RecordInstrument
177
179
  def self.wrap_expr?(node, parent = nil)
178
180
  return false unless node.respond_to?(:location)
179
181
  return false if literal_value_node?(node)
182
+ return false if command_style_call_node?(node)
183
+ return false if parent.is_a?(Prism::DefinedNode)
180
184
  if parent.is_a?(Prism::AliasGlobalVariableNode) || parent.is_a?(Prism::AliasMethodNode)
181
185
  return false
182
186
  end
@@ -196,6 +200,27 @@ module RecordInstrument
196
200
  WRAP_NODE_CLASSES.include?(node.class)
197
201
  end
198
202
 
203
+ def self.command_style_call_node?(node)
204
+ return false unless node.is_a?(Prism::CallNode)
205
+ return false unless node.respond_to?(:arguments) && node.arguments
206
+ return false if node.respond_to?(:opening_loc) && node.opening_loc
207
+
208
+ name = node.respond_to?(:name) ? node.name : nil
209
+ return false unless name
210
+
211
+ IDENTIFIER_METHOD_NAME_RE.match?(name.to_s)
212
+ end
213
+
214
+ def self.instrumentable_child_nodes(node)
215
+ return [] unless node
216
+ return [] if node.is_a?(Prism::DefinedNode)
217
+ if command_style_call_node?(node) && node.respond_to?(:block) && node.block
218
+ [node.block]
219
+ else
220
+ node.child_nodes
221
+ end
222
+ end
223
+
199
224
  def self.expr_location(node)
200
225
  loc = node.location
201
226
  return {
@@ -341,22 +366,31 @@ module RecordInstrument
341
366
 
342
367
  def self.events_from_ids
343
368
  out = []
344
- @events_by_id.each_with_index do |e, id|
345
- next unless e
346
- loc = @loc_by_id[id]
369
+ summary_cache = {}
370
+ @loc_by_id.each_with_index do |loc, id|
347
371
  next unless loc
372
+
373
+ e = @events_by_id[id]
348
374
  case collect_mode
349
375
  when :history
350
- raw_values = values_from_ring(e)
351
- all_types = history_type_set(e)
352
- if all_types.nil? || all_types.empty?
353
- all_types = {}
354
- raw_values.each do |v|
355
- t = value_type_name(v)
356
- all_types[t] = (all_types[t] || 0) + 1
376
+ if e
377
+ raw_values = values_from_ring(e)
378
+ all_types = history_type_set(e)
379
+ if all_types.nil? || all_types.empty?
380
+ all_types = {}
381
+ raw_values.each do |v|
382
+ t = value_type_name(v)
383
+ all_types[t] = (all_types[t] || 0) + 1
384
+ end
357
385
  end
386
+ max = history_ring_size(e)
387
+ total = e[max + 1]
388
+ else
389
+ raw_values = []
390
+ all_types = {}
391
+ total = 0
358
392
  end
359
- max = history_ring_size(e)
393
+
360
394
  out << {
361
395
  file: loc[:file],
362
396
  start_line: loc[:start_line],
@@ -365,9 +399,20 @@ module RecordInstrument
365
399
  end_col: loc[:end_col],
366
400
  kind: loc[:kind].to_s,
367
401
  name: loc[:name],
368
- sampled_values: raw_values.map { |v| summarize_value(v, type: value_type_name(v)) },
402
+ sampled_values: raw_values.map do |v|
403
+ key = v.__id__
404
+ cached = summary_cache[key]
405
+ if cached
406
+ cached.dup
407
+ else
408
+ type = value_type_name(v)
409
+ summary = summarize_value(v, type: type)
410
+ summary_cache[key] = summary
411
+ summary.dup
412
+ end
413
+ end,
369
414
  types: sorted_type_counts(all_types),
370
- total: e[max + 1]
415
+ total: total
371
416
  }
372
417
  when :types
373
418
  out << {
@@ -378,12 +423,12 @@ module RecordInstrument
378
423
  end_col: loc[:end_col],
379
424
  kind: loc[:kind].to_s,
380
425
  name: loc[:name],
381
- types: sorted_type_counts(e[:types]),
382
- total: e[:total]
426
+ types: sorted_type_counts(e ? e[:types] : nil),
427
+ total: e ? e[:total] : 0
383
428
  }
384
429
  else # :last
385
- last_raw = e[:last_value]
386
- last_type = value_type_name(last_raw)
430
+ last_raw = e && e[:last_value]
431
+ last_type = value_type_name(last_raw) if e
387
432
  out << {
388
433
  file: loc[:file],
389
434
  start_line: loc[:start_line],
@@ -392,9 +437,21 @@ module RecordInstrument
392
437
  end_col: loc[:end_col],
393
438
  kind: loc[:kind].to_s,
394
439
  name: loc[:name],
395
- last_value: summarize_value(last_raw, type: last_type),
396
- types: sorted_type_counts(e[:types]),
397
- total: e[:total]
440
+ last_value: if e
441
+ key = last_raw.__id__
442
+ cached = summary_cache[key]
443
+ if cached
444
+ cached.dup
445
+ else
446
+ summary = summarize_value(last_raw, type: last_type)
447
+ summary_cache[key] = summary
448
+ summary.dup
449
+ end
450
+ else
451
+ nil
452
+ end,
453
+ types: sorted_type_counts(e ? e[:types] : nil),
454
+ total: e ? e[:total] : 0
398
455
  }
399
456
  end
400
457
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Lumitrace
4
- VERSION = "0.4.2"
4
+ VERSION = "0.5.0"
5
5
  end
data/lib/lumitrace.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "json"
4
+ require "shellwords"
4
5
  require "tmpdir"
5
6
  require_relative "lumitrace/version"
6
7
  require_relative "lumitrace/record_instrument"
@@ -34,6 +35,16 @@ module Lumitrace
34
35
  $stderr.puts("[lumitrace] #{message}")
35
36
  end
36
37
 
38
+ def self.current_command_text
39
+ argv0 = $0.to_s
40
+ args = Array(ARGV).map(&:to_s)
41
+ parts = []
42
+ parts << argv0 unless argv0.empty?
43
+ parts.concat(args)
44
+ return nil if parts.empty?
45
+ Shellwords.join(parts)
46
+ end
47
+
37
48
  def self.normalize_collect_mode(mode)
38
49
  m = mode.to_s.strip
39
50
  m = "last" if m.empty?
@@ -459,13 +470,22 @@ module Lumitrace
459
470
  next
460
471
  end
461
472
 
473
+ events_collect_start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
462
474
  events = RecordInstrument.events
475
+ events_collect_ms = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - events_collect_start) * 1000.0
476
+ verbose_log(format("events: collect %.1fms count=%d", events_collect_ms, events.length)) if @verbose_level.to_i > 0
477
+
478
+ detail_logger = (@verbose_level.to_i > 1 ? method(:verbose_log) : nil)
479
+
480
+ events_merge_start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
463
481
  events = RecordInstrument.merge_child_events(
464
482
  events,
465
483
  @results_dir,
466
484
  max_samples: @atexit_max_samples,
467
- logger: method(:verbose_log)
485
+ logger: detail_logger
468
486
  )
487
+ events_merge_ms = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - events_merge_start) * 1000.0
488
+ verbose_log(format("events: merge_children %.1fms count=%d", events_merge_ms, events.length)) if @verbose_level.to_i > 0
469
489
 
470
490
  if @atexit_json
471
491
  json_path = @atexit_json == true ? "lumitrace_recorded.json" : @atexit_json
@@ -476,7 +496,7 @@ module Lumitrace
476
496
  end
477
497
  if @atexit_text
478
498
  tty = @atexit_text == true ? $stdout.tty? : false
479
- text = GenerateResultedHtml.render_text_all_from_events(
499
+ text = GenerateResultedHtml.render_text_all_from_normalized_events(
480
500
  events,
481
501
  root: @atexit_output_root,
482
502
  ranges_by_file: @atexit_ranges_by_file,
@@ -493,16 +513,23 @@ module Lumitrace
493
513
  end
494
514
  end
495
515
  if @atexit_html
496
- html = GenerateResultedHtml.render_all_from_events(
516
+ html_render_start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
517
+ html = GenerateResultedHtml.render_all_from_normalized_events(
497
518
  events,
498
519
  root: @atexit_output_root,
499
520
  ranges_by_file: @atexit_ranges_by_file,
500
521
  collect_mode: @atexit_collect_mode,
501
- max_samples: @atexit_max_samples
522
+ max_samples: @atexit_max_samples,
523
+ logger: detail_logger,
524
+ command_text: current_command_text
502
525
  )
526
+ html_render_ms = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - html_render_start) * 1000.0
503
527
  out_path = @atexit_html == true ? "lumitrace_recorded.html" : @atexit_html
504
528
  out_path = File.expand_path(out_path, @atexit_output_root)
529
+ html_write_start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
505
530
  File.write(out_path, html)
531
+ html_write_ms = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - html_write_start) * 1000.0
532
+ verbose_log(format("html: render %.1fms write %.1fms bytes=%d", html_render_ms, html_write_ms, html.bytesize)) if @verbose_level.to_i > 0
506
533
  verbose_log("html: #{out_path}")
507
534
  notify_output_path("html", out_path)
508
535
  end