xcactivitylog 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/lib/slf0/token.rb +25 -16
- data/lib/slf0/tokenizer.rb +10 -4
- data/lib/xcactivitylog/objects.rb +164 -7
- data/lib/xcactivitylog/parser.rb +15 -11
- metadata +7 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 86a13d7e86ed544e21d0bf9a1a84fb981671f8100160ec82fb59ba48ee0eb270
|
4
|
+
data.tar.gz: '0333681b5f17012ba1ac5850256c81965901262a93357449852874afe5bac997'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1eb8f4baed133593e91db5554b0d2dd02e1fd52d5b2f7b1cf2a6e957438490a3cb0349c51ed273b36a533fe5730981f5b5fb2cb692c2673f3a00380c5263b108
|
7
|
+
data.tar.gz: 2beb9f7c54b34ba0c91a37a044732b53d14e7657127a8aa6219ae1bbc057838fe1bf29b0db8ae3eab2480cce5a3e5f44ed62a2c1061866ed9c0d2c71c507a098
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
data/lib/slf0/token.rb
CHANGED
@@ -40,53 +40,62 @@ module SLF0
|
|
40
40
|
@class_deserializer = class_deserializer
|
41
41
|
end
|
42
42
|
|
43
|
-
def int(
|
44
|
-
shift(Int,
|
43
|
+
def int(&reason_blk)
|
44
|
+
shift(Int, &reason_blk).value
|
45
45
|
end
|
46
46
|
|
47
|
-
def string(
|
48
|
-
return if shift_nil?(
|
47
|
+
def string(&reason_blk)
|
48
|
+
return if shift_nil?(&reason_blk)
|
49
49
|
|
50
|
-
shift(String,
|
50
|
+
shift(String, &reason_blk).value
|
51
51
|
end
|
52
52
|
|
53
|
-
def double(
|
53
|
+
def double(&reason_blk)
|
54
54
|
return if shift_nil?
|
55
55
|
|
56
|
-
shift(Double,
|
56
|
+
shift(Double, &reason_blk).value
|
57
57
|
end
|
58
58
|
|
59
|
-
def object_list(
|
60
|
-
return if shift_nil?(
|
59
|
+
def object_list(&reason_blk)
|
60
|
+
return if shift_nil?(&reason_blk)
|
61
61
|
|
62
|
-
shift(ObjectList,
|
62
|
+
length = shift(ObjectList, &reason_blk).length
|
63
|
+
Array.new(length) do
|
64
|
+
object { reason_blk && "object #{length} in object list for #{reason_blk&.call}" }
|
65
|
+
end
|
63
66
|
end
|
64
67
|
|
65
|
-
def object(
|
66
|
-
|
68
|
+
def object(&reason_blk)
|
69
|
+
return if shift_nil?(&reason_blk)
|
70
|
+
|
71
|
+
deserializer_for(shift(ClassNameRef, &reason_blk).value)[self]
|
67
72
|
end
|
68
73
|
|
69
74
|
def deserializer_for(class_ref_num)
|
70
75
|
@class_deserializer[class_ref_num].last
|
71
76
|
end
|
72
77
|
|
73
|
-
def shift_nil?(
|
74
|
-
shift(ObjectListNil,
|
78
|
+
def shift_nil?(&reason_blk)
|
79
|
+
shift(ObjectListNil, raise: false, &reason_blk)
|
75
80
|
end
|
76
81
|
|
77
|
-
def shift(cls,
|
82
|
+
def shift(cls, raise: true, &reason_blk)
|
78
83
|
unless (token = @tokens.shift)
|
79
84
|
raise 'no more tokens'
|
80
85
|
end
|
81
86
|
|
82
87
|
unless token.is_a?(cls)
|
83
|
-
|
88
|
+
unexpected_token!(cls, token, &reason_blk) if raise
|
84
89
|
|
85
90
|
@tokens.unshift(token)
|
86
91
|
return
|
87
92
|
end
|
88
93
|
token
|
89
94
|
end
|
95
|
+
|
96
|
+
def unexpected_token!(expected_class, token, &reason_blk)
|
97
|
+
raise "expected #{expected_class} got #{token.inspect} for #{reason_blk&.call}"
|
98
|
+
end
|
90
99
|
end
|
91
100
|
end
|
92
101
|
end
|
data/lib/slf0/tokenizer.rb
CHANGED
@@ -24,7 +24,7 @@ module SLF0
|
|
24
24
|
def tokenize_body!
|
25
25
|
body = []
|
26
26
|
until scanner.eos?
|
27
|
-
object =
|
27
|
+
object = tokenize_double_field || tokenize_field || tokenize_object_list_nil
|
28
28
|
raise "malformed no object: #{scanner.rest.inspect}\n\nafter: #{body.inspect}" unless object
|
29
29
|
|
30
30
|
body << object
|
@@ -39,11 +39,11 @@ module SLF0
|
|
39
39
|
when '#'
|
40
40
|
SLF0::Token::Int.new int
|
41
41
|
when '%'
|
42
|
-
SLF0::Token::ClassName.new
|
42
|
+
SLF0::Token::ClassName.new scan_length(int).freeze
|
43
43
|
when '@'
|
44
44
|
SLF0::Token::ClassNameRef.new int
|
45
45
|
when '"'
|
46
|
-
SLF0::Token::String.new
|
46
|
+
SLF0::Token::String.new scan_length(int).tr("\r", "\n").freeze
|
47
47
|
when '('
|
48
48
|
SLF0::Token::ObjectList.new int
|
49
49
|
else
|
@@ -52,8 +52,14 @@ module SLF0
|
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
55
|
+
def scan_length(length)
|
56
|
+
string = scanner.string[scanner.pos, length]
|
57
|
+
scanner.pos += length
|
58
|
+
string
|
59
|
+
end
|
60
|
+
|
55
61
|
def tokenize_double_field
|
56
|
-
return unless (hex = scanner.scan(
|
62
|
+
return unless (hex = scanner.scan(/\h*\^/)&.chop)
|
57
63
|
|
58
64
|
double = [hex.to_i(16)].pack('Q<').unpack1('G')
|
59
65
|
SLF0::Token::Double.new double
|
@@ -2,8 +2,8 @@
|
|
2
2
|
|
3
3
|
module XCActivityLog
|
4
4
|
class SerializedObject
|
5
|
-
Attribute = Struct.new(:name, :type, :first_version, :
|
6
|
-
def self.attribute(name, type, first_version = 0,
|
5
|
+
Attribute = Struct.new(:name, :type, :first_version, :first_version_without)
|
6
|
+
def self.attribute(name, type, first_version = 0, first_version_without = 99_999)
|
7
7
|
attr_reader name
|
8
8
|
alias_method "#{name}?", name if type == :boolean
|
9
9
|
if type == :time
|
@@ -12,7 +12,7 @@ module XCActivityLog
|
|
12
12
|
time.to_i * 1_000_000 + time.usec
|
13
13
|
end
|
14
14
|
end
|
15
|
-
attributes << Attribute.new(name, type, first_version,
|
15
|
+
attributes << Attribute.new(name, type, first_version, first_version_without).freeze
|
16
16
|
end
|
17
17
|
|
18
18
|
def self.attributes
|
@@ -47,6 +47,47 @@ module XCActivityLog
|
|
47
47
|
end
|
48
48
|
|
49
49
|
class IDEActivityLogSection < SerializedObject
|
50
|
+
class Severity
|
51
|
+
protected
|
52
|
+
|
53
|
+
attr_reader :severity
|
54
|
+
|
55
|
+
public
|
56
|
+
|
57
|
+
def initialize(severity)
|
58
|
+
@severity = severity
|
59
|
+
freeze
|
60
|
+
end
|
61
|
+
|
62
|
+
SUCCESS = new(0)
|
63
|
+
WARNING = new(1)
|
64
|
+
ERROR = new(2)
|
65
|
+
TEST_FAILURE = new(3)
|
66
|
+
|
67
|
+
def to_s
|
68
|
+
case severity
|
69
|
+
when 0
|
70
|
+
'Success'
|
71
|
+
when 1
|
72
|
+
'Warning'
|
73
|
+
when 2
|
74
|
+
'Error'
|
75
|
+
when 3
|
76
|
+
'Test Failure'
|
77
|
+
else
|
78
|
+
"Unknown (#{severity})"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
include Comparable
|
83
|
+
|
84
|
+
def <=>(other)
|
85
|
+
severity <=> other.severity
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
TargetInfo = Struct.new(:name, :configuration, :workspace, keyword_init: true)
|
90
|
+
|
50
91
|
include Enumerable
|
51
92
|
def each(&blk)
|
52
93
|
return enum_for(__method__) unless block_given?
|
@@ -55,9 +96,82 @@ module XCActivityLog
|
|
55
96
|
subsections&.each { |s| s.each(&blk) }
|
56
97
|
end
|
57
98
|
|
99
|
+
def each_with_parent(parent: nil, &blk)
|
100
|
+
return enum_for(__method__) unless block_given?
|
101
|
+
|
102
|
+
yield self, parent
|
103
|
+
subsections&.each { |s| s.each_with_parent(parent: self, &blk) }
|
104
|
+
end
|
105
|
+
|
58
106
|
def duration_usec
|
59
107
|
time_stopped_recording_usec - time_started_recording_usec
|
60
108
|
end
|
109
|
+
|
110
|
+
def target_info(parent: nil)
|
111
|
+
parent&.target_info ||
|
112
|
+
(title =~ /=== BUILD TARGET (.+?) OF PROJECT (.+?) WITH CONFIGURATION (.+?) ===/ &&
|
113
|
+
TargetInfo.new(name: Regexp.last_match(1), configuration: Regexp.last_match(3), workspace: Regexp.last_match(2)))
|
114
|
+
end
|
115
|
+
|
116
|
+
def each_trace_event
|
117
|
+
thread_id_map_by_section_type = Hash.new { |h, k| h[k] = [] }
|
118
|
+
each_with_parent.sort_by { |s, _| s.time_started_recording }.each do |section, parent|
|
119
|
+
thread_id_map = thread_id_map_by_section_type[section.section_type]
|
120
|
+
best_thread, thread_id = thread_id_map.each_with_index.select do |thread, _tid|
|
121
|
+
section.time_started_recording > thread.last.time_stopped_recording
|
122
|
+
end.min_by do |thread, _tid|
|
123
|
+
(section.time_started_recording - thread.last.time_stopped_recording) +
|
124
|
+
(thread.last.time_stopped_recording - thread_id_map.map(&:last).map(&:time_stopped_recording).min)
|
125
|
+
end
|
126
|
+
unless thread_id
|
127
|
+
thread_id = thread_id_map.size
|
128
|
+
best_thread = []
|
129
|
+
thread_id_map << best_thread
|
130
|
+
end
|
131
|
+
best_thread << section
|
132
|
+
|
133
|
+
yield(section: section, parent: parent, thread_id: thread_id)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def write_chrome_trace_file(section_type:, to:)
|
138
|
+
to << '{"traceEvents":[' << "\n"
|
139
|
+
written_comma = false
|
140
|
+
each_trace_event do |section:, parent:, thread_id:|
|
141
|
+
case section.section_type
|
142
|
+
when section_type
|
143
|
+
if written_comma
|
144
|
+
to << ",\n"
|
145
|
+
else
|
146
|
+
written_comma = true
|
147
|
+
end
|
148
|
+
require 'json'
|
149
|
+
to << JSON.generate(
|
150
|
+
pid: section.section_type.to_s,
|
151
|
+
tid: thread_id,
|
152
|
+
ts: section.time_started_recording_usec,
|
153
|
+
ph: 'X',
|
154
|
+
name: section.title.dup&.force_encoding('UTF-8'),
|
155
|
+
dur: section.duration_usec,
|
156
|
+
args: {
|
157
|
+
subtitle: section.subtitle.dup&.force_encoding('UTF-8'),
|
158
|
+
target: section.target_info(parent: parent).to_h,
|
159
|
+
severity: section.severity
|
160
|
+
}
|
161
|
+
)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
to << "\n]}"
|
166
|
+
|
167
|
+
to
|
168
|
+
end
|
169
|
+
|
170
|
+
def severity
|
171
|
+
severity = (messages || []).reduce(Severity::SUCCESS) { |a, e| [a, Severity.new(e.severity)].max }
|
172
|
+
(subsections || []).reduce(severity) { |a, e| [a, e.severity].max }
|
173
|
+
end
|
174
|
+
|
61
175
|
attribute :section_type, :int
|
62
176
|
attribute :domain_type, :string
|
63
177
|
attribute :title, :string
|
@@ -75,13 +189,28 @@ module XCActivityLog
|
|
75
189
|
attribute :command_detail_description, :string
|
76
190
|
attribute :unique_identifier, :string
|
77
191
|
attribute :localized_result_string, :string
|
78
|
-
attribute :xcbuild_signature, :string
|
79
|
-
attribute :collect_metrics, :boolean, 9
|
192
|
+
attribute :xcbuild_signature, :string, 8
|
193
|
+
attribute :collect_metrics, :boolean, 9, 10
|
80
194
|
attributes.freeze
|
81
195
|
end
|
82
196
|
class IDECommandLineBuildLog < IDEActivityLogSection
|
83
197
|
attributes.freeze
|
84
198
|
end
|
199
|
+
class IDEActivityLogCommandInvocationSection < IDEActivityLogSection
|
200
|
+
attributes.freeze
|
201
|
+
end
|
202
|
+
class IDEActivityLogUnitTestSection < IDEActivityLogSection
|
203
|
+
attribute :tests_passes, :string
|
204
|
+
attribute :duration, :string
|
205
|
+
attribute :summary, :string
|
206
|
+
attribute :suite_name, :string
|
207
|
+
attribute :test_name, :string
|
208
|
+
attribute :performance_test_output, :string
|
209
|
+
attributes.freeze
|
210
|
+
end
|
211
|
+
class IDEActivityLogMajorGroupSection < IDEActivityLogSection
|
212
|
+
attributes.freeze
|
213
|
+
end
|
85
214
|
|
86
215
|
class IDEActivityLogMessage < SerializedObject
|
87
216
|
include Enumerable
|
@@ -98,7 +227,7 @@ module XCActivityLog
|
|
98
227
|
attribute :submessages, :object_list
|
99
228
|
attribute :severity, :int
|
100
229
|
attribute :type, :string
|
101
|
-
attribute :location, :
|
230
|
+
attribute :location, :document_location
|
102
231
|
attribute :category_identifier, :string
|
103
232
|
attribute :secondary_locations, :object_list
|
104
233
|
attribute :additional_description, :string
|
@@ -107,6 +236,34 @@ module XCActivityLog
|
|
107
236
|
class IDEClangDiagnosticActivityLogMessage < IDEActivityLogMessage
|
108
237
|
attributes.freeze
|
109
238
|
end
|
239
|
+
class IDEActivityLogAnalyzerResultMessage < IDEActivityLogMessage
|
240
|
+
attribute :result_type, :string
|
241
|
+
attribute :key_event_index, :int
|
242
|
+
attributes.freeze
|
243
|
+
end
|
244
|
+
class IDEActivityLogAnalyzerStepMessage < IDEActivityLogMessage
|
245
|
+
attribute :parent_index, :int
|
246
|
+
attributes.freeze
|
247
|
+
end
|
248
|
+
class IDEActivityLogAnalyzerEventStepMessage < IDEActivityLogAnalyzerStepMessage
|
249
|
+
attribute :result_type, :string
|
250
|
+
attribute :key_event_index, :int
|
251
|
+
attributes.freeze
|
252
|
+
end
|
253
|
+
class IDEActivityLogAnalyzerControlFlowStepMessage < IDEActivityLogAnalyzerStepMessage
|
254
|
+
attribute :end_location, :document_location
|
255
|
+
attribute :edges, :object_list
|
256
|
+
attributes.freeze
|
257
|
+
end
|
258
|
+
class IDEActivityLogAnalyzerWarningMessage < IDEActivityLogMessage
|
259
|
+
attributes.freeze
|
260
|
+
end
|
261
|
+
|
262
|
+
class IDEActivityLogAnalyzerControlFlowStepEdge < SerializedObject
|
263
|
+
attribute :start_location, :document_location
|
264
|
+
attribute :end_location, :document_location
|
265
|
+
attributes.freeze
|
266
|
+
end
|
110
267
|
|
111
268
|
class DVTDocumentLocation < SerializedObject
|
112
269
|
attribute :document_url_string, :string
|
@@ -123,7 +280,7 @@ module XCActivityLog
|
|
123
280
|
attribute :ending_line_number, :int
|
124
281
|
attribute :ending_column_number, :int
|
125
282
|
attribute :character_range, :nsrange
|
126
|
-
attribute :location_encoding, :int
|
283
|
+
attribute :location_encoding, :int, 7
|
127
284
|
attributes.freeze
|
128
285
|
end
|
129
286
|
end
|
data/lib/xcactivitylog/parser.rb
CHANGED
@@ -8,7 +8,7 @@ module XCActivityLog
|
|
8
8
|
class S < SLF0::Token::Stream
|
9
9
|
def initialize(tokens, class_deserializer:)
|
10
10
|
super(tokens, class_deserializer: class_deserializer)
|
11
|
-
@version = int
|
11
|
+
@version = int { 'activity log version' }
|
12
12
|
end
|
13
13
|
|
14
14
|
def deserializer_for(class_ref_num)
|
@@ -24,34 +24,38 @@ module XCActivityLog
|
|
24
24
|
def deserialize_instance_of(stream, cls)
|
25
25
|
instance = cls.new
|
26
26
|
cls.attributes.each do |attr|
|
27
|
-
next if attr.first_version > @version || attr.
|
27
|
+
next if attr.first_version > @version || attr.first_version_without <= @version
|
28
28
|
|
29
|
-
value = stream.send(attr.type
|
29
|
+
value = stream.send(attr.type) { "#{attr.name} for #{cls.name.split('::').last} #{instance.inspect}" }
|
30
30
|
instance.instance_variable_set(:"@#{attr.name}", value)
|
31
31
|
end
|
32
32
|
instance.freeze
|
33
33
|
end
|
34
34
|
|
35
|
-
def boolean(
|
36
|
-
int(
|
35
|
+
def boolean(&reason_blk)
|
36
|
+
int(&reason_blk) != 0
|
37
37
|
end
|
38
38
|
|
39
|
-
def nsrange(
|
39
|
+
def nsrange(&_reason_blk)
|
40
40
|
deserialize_instance_of(self, NSRange)
|
41
41
|
end
|
42
42
|
|
43
|
-
def document_location(
|
43
|
+
def document_location(&reason_blk)
|
44
44
|
return if shift_nil?
|
45
45
|
|
46
|
-
object(
|
47
|
-
raise "expected location, got #{o.class.name} for #{
|
46
|
+
object(&reason_blk).tap do |o|
|
47
|
+
raise "expected location, got #{o.class.name} for #{reason_blk&.call}" unless o.is_a?(DVTDocumentLocation)
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
51
51
|
EPOCH = Time.new(2001, 1, 1, 0, 0, 0, '+00:00').freeze
|
52
52
|
|
53
|
-
def time(
|
54
|
-
EPOCH.+(double(
|
53
|
+
def time(&reason_blk)
|
54
|
+
EPOCH.+(double(&reason_blk)).freeze
|
55
|
+
end
|
56
|
+
|
57
|
+
def unexpected_token!(expected_class, token, &reason_blk)
|
58
|
+
raise "expected #{expected_class} got #{token.inspect} for #{reason_blk&.call} (XCActivityLog version #{@version})"
|
55
59
|
end
|
56
60
|
end
|
57
61
|
def make_stream(tokens, class_deserializer)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: xcactivitylog
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Giddins
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-08-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -52,7 +52,7 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '3.0'
|
55
|
-
description:
|
55
|
+
description:
|
56
56
|
email:
|
57
57
|
- segiddins@segiddins.me
|
58
58
|
executables: []
|
@@ -76,7 +76,7 @@ licenses:
|
|
76
76
|
metadata:
|
77
77
|
homepage_uri: https://github.com/segiddins/xcactivitylog
|
78
78
|
source_code_uri: https://github.com/segiddins/xcactivitylog
|
79
|
-
post_install_message:
|
79
|
+
post_install_message:
|
80
80
|
rdoc_options: []
|
81
81
|
require_paths:
|
82
82
|
- lib
|
@@ -91,9 +91,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
91
91
|
- !ruby/object:Gem::Version
|
92
92
|
version: '0'
|
93
93
|
requirements: []
|
94
|
-
|
95
|
-
|
96
|
-
signing_key:
|
94
|
+
rubygems_version: 3.0.1
|
95
|
+
signing_key:
|
97
96
|
specification_version: 4
|
98
97
|
summary: Parse Xcode's xcactivitylog files (and other SLF0-serialized files)
|
99
98
|
test_files: []
|