xcactivitylog 0.1.0 → 0.2.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.
- 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: []
|