tracetool 0.4.0 → 0.4.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 28053aff3e16e9baee1198e08bdbeffdefeef2f0
4
- data.tar.gz: 894ac01ee7333ee6b02dc5fb422114f6269a0a72
2
+ SHA256:
3
+ metadata.gz: ebe6ba1b66d5457e530be1544b059cf42da169d948a651766ad57fd3c6dac99e
4
+ data.tar.gz: 7b1a2cdefc535a9bb851dc212b69eaa34d89720d051936ab7d34442384db150f
5
5
  SHA512:
6
- metadata.gz: 3fa1e1578d5c6802e508cea8b7c1af90ff2ef4f83f3574b7cf0b218afbcc48c47783706bec06273a1a6887470ebddd0ccbd15fb0448d921296033ce174b064d1
7
- data.tar.gz: 338ed267bce0c0d91ecdb47cdfe6d1f8eeed36179717bc16a6c5362c84fb15d5f3099906e35ad0eaa90af7368c7e8fd2bd1c5bcb1d66b69bcb00265c4330874d
6
+ metadata.gz: d7a9e46d22089eb1b2517edc7664e3bf26ae04e6f82e4b55aaad3e87d9119b293a9d1fdde0a8d072ebadf2ac619487c24ddf099a0184a968e638b90ba34008b3
7
+ data.tar.gz: 4a187b23b271f395919265da64dc8d328b7a1b4fa4bcaf8383d33ed8e07d6c8c5dd5253e044db2d70a3efa6e6c604271b49439f7f4d01f163a5fab04761b8c9c
@@ -16,6 +16,7 @@ module Tracetool
16
16
  # Find scanner which matches trace format
17
17
  @scanner = SCANNERS.map { |s| s[trace] }.compact.first
18
18
  raise(ArgumentError, "#{trace}\n not android trace?") unless @scanner
19
+
19
20
  @scanner.process(context)
20
21
  end
21
22
 
@@ -25,6 +26,7 @@ module Tracetool
25
26
  # Or `nil`. If there was no scanning.
26
27
  def parser(files)
27
28
  return unless @scanner
29
+
28
30
  @scanner.parser(files)
29
31
  end
30
32
  end
@@ -5,9 +5,9 @@ module Tracetool
5
5
  # Parses java stack traces
6
6
  class JavaTraceParser < Tracetool::BaseTraceParser
7
7
  # Describes java stack entry
8
- STACK_ENTRY_PATTERN = /^(\s+at (?<call_description>.+))|((?<error>.+?): (?<message>.+))$/
8
+ STACK_ENTRY_PATTERN = /^(\s+at (?<call_description>.+))|((?<error>.+?): (?<message>.+))$/.freeze
9
9
  # Describes java method call
10
- CALL_PATTERN = /(?<class>.+)\.(?<method>[^\(]+)\((((?<file>.+\.java):(?<line>\d+))|(?<location>.+))\)$/
10
+ CALL_PATTERN = /(?<class>.+)\.(?<method>[^\(]+)\((((?<file>.+\.java):(?<line>\d+))|(?<location>.+))\)$/.freeze
11
11
 
12
12
  def initialize(files)
13
13
  super(STACK_ENTRY_PATTERN, CALL_PATTERN, files, true)
@@ -15,8 +15,14 @@ module Tracetool
15
15
  end
16
16
  # Processes java traces
17
17
  class JavaTraceScanner
18
- RX_FIRST_EXCEPTION_LINE = /^.+$/
19
- RX_OTHER_EXCEPTION_LINE = /at [^(]+\(([^:]+:\d+)|(Native Method)\)$/
18
+ # Usually java trace starts with
19
+ # com.something.SomeClass(: Some message)?
20
+ RX_FIRST_EXCEPTION_LINE = /^([a-zA-Z.]*)(:.*)?$/.freeze
21
+
22
+ # Rest is expanded as
23
+ # at com.other.OtherClass.someMethod(OtherClass.java:42)
24
+ # Source marker can be just "Native Method" or "Unknown Source"
25
+ RX_OTHER_EXCEPTION_LINE = /((at [a-zA-Z$.]+)|(Caused by:)|(\.\.\. [0-9]* more))(.+)?$/.freeze
20
26
 
21
27
  def initialize(string)
22
28
  @trace = string
@@ -38,8 +44,6 @@ module Tracetool
38
44
  def match(string)
39
45
  # Split into lines
40
46
  first, *rest = string.split("\n")
41
-
42
- return if rest.nil? || rest.empty?
43
47
  return unless RX_FIRST_EXCEPTION_LINE.match(first)
44
48
 
45
49
  rest.all? { |line| RX_OTHER_EXCEPTION_LINE.match(line) }
@@ -6,9 +6,13 @@ module Tracetool
6
6
  class NativeTraceParser < Tracetool::BaseTraceParser
7
7
  # Describes android stack entry
8
8
  STACK_ENTRY_PATTERN =
9
- %r{Stack frame #(?<frame>\d+) (?<address>\w+ [a-f\d]+) (?<lib>[/\w\d\.-]+)( )?(: (?<call_description>.+))?$}
9
+ %r{Stack frame #(?<frame>\d+) (?<address>\w+ [a-f\d]+) (?<lib>[/\w\d\.=-]+)( )?(:? (?<call_description>.+))?$}
10
+ .freeze
10
11
  # Describes android native method call (class::method and source file with line number)
11
- CALL_PATTERN = /(Routine )?(?<method>.+) ((in)|(at)) (?<file>.+):(?<line>\d+)/
12
+ CALL_PATTERN = [
13
+ /((Routine )?(?<method>.+) ((in)|(at)) (?<file>.+):(?<line>\d+))/,
14
+ /(?<method>.+?) \d+/
15
+ ].freeze
12
16
 
13
17
  def initialize(files)
14
18
  super(STACK_ENTRY_PATTERN, CALL_PATTERN, files, true)
@@ -30,7 +34,9 @@ module Tracetool
30
34
  # @param [String] trace packed stack trace
31
35
  # @return well formed stack trace
32
36
  def unpack(trace)
33
- dump_body = prepare(trace).map.with_index { |line, index| convert_line(line, index) }
37
+ dump_body = prepare(trace)
38
+ .map
39
+ .with_index { |line, index| convert_line(line, index) }
34
40
  add_header(dump_body.join("\n"))
35
41
  end
36
42
 
@@ -48,8 +54,9 @@ module Tracetool
48
54
  def convert_line(line, index)
49
55
  frame = index
50
56
  addr = line[/^(-?\d+) (.*)$/, 1]
51
- lib = line[/^(-?\d+) (.*)$/, 2].strip
52
- ' #%02i pc %08x %s'.format(frame, addr, lib)
57
+ lib = (line[/^(-?\d+) (.*)$/, 2] || '').strip # nil safe
58
+ ' #%02<frame>i pc %08<addr>x %<lib>s'
59
+ .format(frame: frame, addr: addr, lib: lib)
53
60
  end
54
61
 
55
62
  # If needed here we'll drop all unneeded leading characters from each
@@ -63,14 +70,15 @@ module Tracetool
63
70
  # Processes native traces
64
71
  class NativeTraceScanner
65
72
  # Initial sequence of asterisks which marks begining of trace body
66
- TRACE_DELIMETER = '*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***'.freeze
67
- RX_INITIAL_ASTERISKS = /#{TRACE_DELIMETER.gsub('*', '\*')}/
73
+ TRACE_DELIMETER =
74
+ '*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***'.freeze
75
+ RX_INITIAL_ASTERISKS = /#{TRACE_DELIMETER.gsub('*', '\*')}/.freeze
68
76
  # Contains address line like
69
77
  #
70
78
  # ```
71
79
  # pc 00000000004321ec libfoo.so
72
80
  # ```
73
- RX_PC_ADDRESS = /pc \d+/
81
+ RX_PC_ADDRESS = /pc \d+/.freeze
74
82
 
75
83
  # Format of packed trace.
76
84
  # Consists of one or more trace blocks.
@@ -84,7 +92,7 @@ module Tracetool
84
92
  # ** symbol offset `/\d+/`
85
93
  #
86
94
  # Last two entries can be missing.
87
- RX_PACKED_FORMAT = /^(<<<(\d+ [^ ]+ ([^ ]+ \d+)?;)+>>>)+$/
95
+ RX_PACKED_FORMAT = /^(<<<([-\d]+ [^ ]+ (.+)?;)+>>>)+$/.freeze
88
96
 
89
97
  # @param [String] string well formed native android stack trace
90
98
  # @see https://developer.android.com/ndk/guides/ndk-stack.html
@@ -5,10 +5,11 @@ module Tracetool
5
5
  # IOS traces scanner and source mapper
6
6
  class IOSTraceParser < Tracetool::BaseTraceParser
7
7
  # Describes IOS stack entry
8
- STACK_ENTRY_PATTERN = /^(?<frame>\d+) (?<binary>[^ ]+) (?<call_description>.+)$/
8
+ STACK_ENTRY_PATTERN = /^(?<frame>\d+) (?<binary>[^ ]+) (?<call_description>.+)$/.freeze
9
9
  # Describes source block
10
10
  SOURCE_PATTERN =
11
11
  /^((-?\[(?<class>[^ ]+) (?<method>.+)\])|(?<method>.+)) \(in (?<module>.+)\) \((?<file>.+):(?<line>\d+)\)$/
12
+ .freeze
12
13
 
13
14
  def initialize(files)
14
15
  super(STACK_ENTRY_PATTERN, SOURCE_PATTERN, files, true)
@@ -8,7 +8,6 @@ module Tracetool
8
8
  # List of required argument names
9
9
  REQUIRED_ARGUMENTS = %i[load_address xarchive module_name].freeze
10
10
 
11
- #
12
11
  def initialize(ctx)
13
12
  check_arguments(ctx)
14
13
  @load_address = ctx.load_address
@@ -27,9 +27,10 @@ module Tracetool
27
27
 
28
28
  def self.check_ios(options)
29
29
  return unless options.platform == :ios
30
+
30
31
  {
31
32
  'address' => options.address,
32
- 'module' => options.modulename
33
+ 'module' => options.modulename
33
34
  }.each { |arg, check| raise(OptionParser::MissingArgument, arg) unless check }
34
35
  end
35
36
 
@@ -1,12 +1,16 @@
1
+ require_relative 'string'
2
+
1
3
  module Tracetool
2
4
  # Base trace parser logic
3
5
  class BaseTraceParser
6
+ include StringUtils
7
+
4
8
  attr_reader :entry_pattern, :call_pattern
5
9
 
6
10
  def initialize(entry_pattern, call_pattern, build_files, convert_numbers = false)
7
11
  @build_files = build_files
8
12
  @entry_pattern = entry_pattern
9
- @call_pattern = call_pattern
13
+ @call_pattern = call_pattern.is_a?(Array) ? call_pattern : [call_pattern]
10
14
  @convert_numbers = convert_numbers
11
15
  end
12
16
 
@@ -54,36 +58,63 @@ module Tracetool
54
58
  # * method
55
59
  # * file
56
60
  # * line number
57
- def scan_call(e)
58
- if e[:call_description]
59
- call_pattern.match(e[:call_description]) do |m|
60
- call = extract_groups(m)
61
- # Update file entry with expanded path
62
- call[:file] = find_file(call[:file]) if call[:file]
63
-
64
- e[:call] = call
65
- end
61
+ def scan_call(call_info)
62
+ call_description = call_info[:call_description]
63
+ # TODO: Lazy check
64
+ match = call_description && call_pattern.map { |p| p.match(call_description) }.compact.first
65
+ if match
66
+ call = extract_groups(match)
67
+ # Update file entry with expanded path
68
+ call[:file] = find_file(call[:file]) if call[:file]
69
+
70
+ call_info[:call] = call
66
71
  end
67
72
 
68
- e
73
+ call_info
69
74
  end
70
75
 
71
76
  # Find file with specified file name in symbols dir
72
- # Can return multiple files if name was ambigous
77
+ # Can return multiple files if name was ambiguous
73
78
  def find_file(file)
74
- # Find all matching files
75
- # remove build_dir from path
76
- # remove leading '/'
77
- glob = File.join('**', File.basename(file))
78
- files = @build_files.select { |f| File.fnmatch(glob, f) }
79
+ file_name = File.basename(file)
80
+ # Firstly we'll drop obvious mismatches where basename of file differs
81
+ candidates = @build_files.select { |path| File.basename(path) == file_name }
82
+ # In case when got ambiguous files return all try to find closest match
83
+ files = find_closest_files(file, candidates)
79
84
 
80
85
  # If has only option return first
81
86
  return files.first if files.size == 1
82
87
  # Return original file if files empty
83
88
  return file if files.empty?
84
89
 
85
- # If got ambiguous files return all
86
- files
90
+ files # Return all files if many matched
91
+ end
92
+
93
+ # Select from candidates list such files
94
+ # that ends with maximum substring of file
95
+ # @param [String] file file path to match
96
+ # @param [Array<String>] candidates list of candidates path
97
+ # @return [Array<String>] list of files with maximum length matches
98
+ def find_closest_files(file, candidates)
99
+ candidates.inject([[], 0]) do |acc, elem|
100
+ # Current element score is length of longest common postfix
101
+ elem_score = file.longest_common_postfix(elem).length
102
+
103
+ # Unpack accumulator as (list_of_matched_files, max_score)
104
+ matched, score = acc
105
+ # Will update if only have better score
106
+ if elem_score >= score
107
+ # Current score more than last known score, so now
108
+ # we drop all previous results and replace them with
109
+ # current element
110
+ matched = [] if elem_score > score
111
+ score = elem_score
112
+ # Update list of matched
113
+ matched << elem
114
+ end
115
+
116
+ [matched, score]
117
+ end.first
87
118
  end
88
119
 
89
120
  def extract_groups(match)
@@ -0,0 +1,24 @@
1
+ module Tracetool
2
+ # Set of utility methods for working with strings
3
+ module StringUtils
4
+ # Extended string class
5
+ # rubocop:disable Style/ClassAndModuleChildren
6
+ class ::String
7
+ # Return longest common postfix
8
+ # @param [String] other other string to match
9
+ # @return [String] longest common postfix
10
+ def longest_common_postfix(other)
11
+ sidx = length - 1
12
+ oidx = other.length - 1
13
+
14
+ while sidx >= 0 && oidx >= 0 && (self[sidx] == other[oidx])
15
+ sidx -= 1
16
+ oidx -= 1
17
+ end
18
+
19
+ other[(oidx + 1)..-1]
20
+ end
21
+ end
22
+ # rubocop:enable Style/ClassAndModuleChildren
23
+ end
24
+ end
data/lib/version.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  module Tracetool
2
2
  # Version constant
3
3
  module Version
4
- VERSION = [0, 4, 0].freeze
4
+ VERSION = [0, 4, 1].freeze
5
5
 
6
6
  class << self
7
7
  # @return [String] version string
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tracetool
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - ilya.arkhanhelsky
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-11-27 00:00:00.000000000 Z
11
+ date: 2019-01-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: powerpack
@@ -43,6 +43,7 @@ files:
43
43
  - lib/tracetool/utils/env.rb
44
44
  - lib/tracetool/utils/parser.rb
45
45
  - lib/tracetool/utils/pipe.rb
46
+ - lib/tracetool/utils/string.rb
46
47
  - lib/tracetool_cli.rb
47
48
  - lib/version.rb
48
49
  homepage: https://github.com/vizor-games/tracetool
@@ -65,7 +66,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
65
66
  version: '0'
66
67
  requirements: []
67
68
  rubyforge_project:
68
- rubygems_version: 2.6.13
69
+ rubygems_version: 2.7.8
69
70
  signing_key:
70
71
  specification_version: 4
71
72
  summary: Tracetool