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 +5 -5
- data/lib/tracetool/android.rb +2 -0
- data/lib/tracetool/android/java.rb +10 -6
- data/lib/tracetool/android/native.rb +17 -9
- data/lib/tracetool/ios/parser.rb +2 -1
- data/lib/tracetool/ios/scanner.rb +0 -1
- data/lib/tracetool/utils/cli.rb +2 -1
- data/lib/tracetool/utils/parser.rb +50 -19
- data/lib/tracetool/utils/string.rb +24 -0
- data/lib/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: ebe6ba1b66d5457e530be1544b059cf42da169d948a651766ad57fd3c6dac99e
|
4
|
+
data.tar.gz: 7b1a2cdefc535a9bb851dc212b69eaa34d89720d051936ab7d34442384db150f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d7a9e46d22089eb1b2517edc7664e3bf26ae04e6f82e4b55aaad3e87d9119b293a9d1fdde0a8d072ebadf2ac619487c24ddf099a0184a968e638b90ba34008b3
|
7
|
+
data.tar.gz: 4a187b23b271f395919265da64dc8d328b7a1b4fa4bcaf8383d33ed8e07d6c8c5dd5253e044db2d70a3efa6e6c604271b49439f7f4d01f163a5fab04761b8c9c
|
data/lib/tracetool/android.rb
CHANGED
@@ -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
|
-
|
19
|
-
|
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
|
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 =
|
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)
|
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
|
-
' #%
|
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 =
|
67
|
-
|
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 = /^(<<<(
|
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
|
data/lib/tracetool/ios/parser.rb
CHANGED
@@ -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)
|
data/lib/tracetool/utils/cli.rb
CHANGED
@@ -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' =>
|
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(
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
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
|
-
|
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
|
77
|
+
# Can return multiple files if name was ambiguous
|
73
78
|
def find_file(file)
|
74
|
-
|
75
|
-
#
|
76
|
-
|
77
|
-
|
78
|
-
files =
|
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
|
-
#
|
86
|
-
|
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
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.
|
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:
|
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.
|
69
|
+
rubygems_version: 2.7.8
|
69
70
|
signing_key:
|
70
71
|
specification_version: 4
|
71
72
|
summary: Tracetool
|