tracetool 0.4.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
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