Skip to content

Commit 0e030f9

Browse files
authored
Add the ability to read persisted (.clsrecord) crash files (#4762)
1 parent 34f720c commit 0e030f9

30 files changed

+1102
-0
lines changed
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Copyright 2020 Google
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#import <Foundation/Foundation.h>
18+
19+
/**
20+
* The class will be responsible for aggregating the data from the persisted crash files
21+
* and returning a report object used for FireLog.
22+
**/
23+
@interface FIRCLSRecordAdapter : NSObject
24+
25+
- (instancetype)init NS_UNAVAILABLE;
26+
27+
/// Initializer
28+
/// @param folderPath Path where the persisted crash files reside
29+
- (instancetype)initWithPath:(NSString *)folderPath;
30+
31+
// TODO: Add function to return the nanopb/FireLog report
32+
33+
@end
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
/*
2+
* Copyright 2020 Google
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#import "FIRCLSRecordAdapter.h"
18+
19+
#import "FIRCLSInternalReport.h"
20+
#import "FIRCLSLogger.h"
21+
#import "FIRCLSRecordApplication.h"
22+
#import "FIRCLSRecordBinaryImage.h"
23+
#import "FIRCLSRecordExecutable.h"
24+
#import "FIRCLSRecordHost.h"
25+
#import "FIRCLSRecordIdentity.h"
26+
#import "FIRCLSRecordKeyValue.h"
27+
#import "FIRCLSRecordProcessStats.h"
28+
#import "FIRCLSRecordRegister.h"
29+
#import "FIRCLSRecordRuntime.h"
30+
#import "FIRCLSRecordSignal.h"
31+
#import "FIRCLSRecordStorage.h"
32+
#import "FIRCLSRecordThread.h"
33+
34+
@interface FIRCLSRecordAdapter ()
35+
36+
@property(nonatomic, strong) NSString *folderPath;
37+
38+
@property(nonatomic, strong) FIRCLSRecordSignal *signal;
39+
@property(nonatomic, strong) NSArray<FIRCLSRecordThread *> *threads;
40+
@property(nonatomic, strong) FIRCLSRecordProcessStats *processStats;
41+
@property(nonatomic, strong) FIRCLSRecordStorage *storage;
42+
@property(nonatomic, strong) NSArray<FIRCLSRecordBinaryImage *> *binaryImages;
43+
@property(nonatomic, strong) FIRCLSRecordRuntime *runtime;
44+
@property(nonatomic, strong) FIRCLSRecordIdentity *identity;
45+
@property(nonatomic, strong) FIRCLSRecordHost *host;
46+
@property(nonatomic, strong) FIRCLSRecordApplication *application;
47+
@property(nonatomic, strong) FIRCLSRecordExecutable *executable;
48+
@property(nonatomic, strong) NSArray<FIRCLSRecordKeyValue *> *keyValues;
49+
50+
@end
51+
52+
@implementation FIRCLSRecordAdapter
53+
54+
- (instancetype)initWithPath:(NSString *)folderPath {
55+
self = [super init];
56+
if (self) {
57+
_folderPath = folderPath;
58+
59+
[self loadBinaryImagesFile];
60+
[self loadMetaDataFile];
61+
[self loadSignalFile];
62+
[self loadKeyValuesFile];
63+
}
64+
return self;
65+
}
66+
67+
- (void)loadBinaryImagesFile {
68+
NSString *path = [self.folderPath stringByAppendingPathComponent:CLSReportBinaryImageFile];
69+
self.binaryImages = [FIRCLSRecordBinaryImage
70+
binaryImagesFromDictionaries:[FIRCLSRecordAdapter dictionariesFromEachLineOfFile:path]];
71+
}
72+
73+
- (void)loadMetaDataFile {
74+
NSString *path = [self.folderPath stringByAppendingPathComponent:CLSReportMetadataFile];
75+
NSDictionary *dict = [FIRCLSRecordAdapter combinedDictionariesFromFilePath:path];
76+
77+
self.identity = [[FIRCLSRecordIdentity alloc] initWithDict:dict[@"identity"]];
78+
self.host = [[FIRCLSRecordHost alloc] initWithDict:dict[@"host"]];
79+
self.application = [[FIRCLSRecordApplication alloc] initWithDict:dict[@"application"]];
80+
self.executable = [[FIRCLSRecordExecutable alloc] initWithDict:dict[@"executable"]];
81+
}
82+
83+
- (void)loadSignalFile {
84+
NSString *path = [self.folderPath stringByAppendingPathComponent:CLSReportSignalFile];
85+
NSDictionary *dicts = [FIRCLSRecordAdapter combinedDictionariesFromFilePath:path];
86+
87+
self.signal = [[FIRCLSRecordSignal alloc] initWithDict:dicts[@"signal"]];
88+
self.runtime = [[FIRCLSRecordRuntime alloc] initWithDict:dicts[@"runtime"]];
89+
self.processStats = [[FIRCLSRecordProcessStats alloc] initWithDict:dicts[@"process_stats"]];
90+
self.storage = [[FIRCLSRecordStorage alloc] initWithDict:dicts[@"storage"]];
91+
92+
// The thread's objc_selector_name is set with the runtime's info
93+
self.threads = [FIRCLSRecordThread threadsFromDictionaries:dicts[@"threads"]
94+
withNames:dicts[@"thread_names"]
95+
withRuntime:self.runtime];
96+
}
97+
98+
- (void)loadKeyValuesFile {
99+
NSString *path =
100+
[self.folderPath stringByAppendingPathComponent:CLSReportInternalIncrementalKVFile];
101+
self.keyValues = [FIRCLSRecordKeyValue
102+
keyValuesFromDictionaries:[FIRCLSRecordAdapter dictionariesFromEachLineOfFile:path]];
103+
}
104+
105+
/// Return the persisted crash file as a combined dictionary that way lookups can occur with a key
106+
/// (to avoid ordering dependency)
107+
/// @param filePath Persisted crash file path
108+
+ (NSDictionary *)combinedDictionariesFromFilePath:(NSString *)filePath {
109+
NSMutableDictionary *joinedDict = [[NSMutableDictionary alloc] init];
110+
for (NSDictionary *dict in [self dictionariesFromEachLineOfFile:filePath]) {
111+
[joinedDict addEntriesFromDictionary:dict];
112+
}
113+
return joinedDict;
114+
}
115+
116+
/// The persisted crash files contains JSON on separate lines. Read each line and return the JSON
117+
/// data as a dictionary.
118+
/// @param filePath Persisted crash file path
119+
+ (NSArray<NSDictionary *> *)dictionariesFromEachLineOfFile:(NSString *)filePath {
120+
NSString *content = [[NSString alloc] initWithContentsOfFile:filePath
121+
encoding:NSUTF8StringEncoding
122+
error:nil];
123+
NSArray *lines =
124+
[content componentsSeparatedByCharactersInSet:NSCharacterSet.newlineCharacterSet];
125+
126+
NSMutableArray<NSDictionary *> *array = [[NSMutableArray<NSDictionary *> alloc] init];
127+
128+
int lineNum = 1;
129+
for (NSString *line in lines) {
130+
NSError *error;
131+
NSDictionary *dict =
132+
[NSJSONSerialization JSONObjectWithData:[line dataUsingEncoding:NSUTF8StringEncoding]
133+
options:0
134+
error:&error];
135+
136+
if (error) {
137+
FIRCLSErrorLog(@"Failed to read JSON from file (%@) line (%d) with error: %@", filePath,
138+
lineNum, error);
139+
} else {
140+
[array addObject:dict];
141+
}
142+
143+
lineNum++;
144+
}
145+
146+
return array;
147+
}
148+
149+
@end
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright 2020 Google
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#import "FIRCLSRecordBase.h"
18+
19+
@interface FIRCLSRecordApplication : FIRCLSRecordBase
20+
21+
@property(nonatomic, copy) NSString *bundle_id;
22+
@property(nonatomic, copy) NSString *build_version;
23+
@property(nonatomic, copy) NSString *display_version;
24+
25+
@end
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright 2020 Google
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#import "FIRCLSRecordApplication.h"
18+
19+
@implementation FIRCLSRecordApplication
20+
21+
- (instancetype)initWithDict:(NSDictionary *)dict {
22+
self = [super initWithDict:dict];
23+
if (self) {
24+
_bundle_id = dict[@"bundle_id"];
25+
_display_version = dict[@"display_version"];
26+
_build_version = dict[@"build_version"];
27+
}
28+
return self;
29+
}
30+
31+
@end
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright 2020 Google
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#import <Foundation/Foundation.h>
18+
19+
/**
20+
* This is the base class to represent the data in the persisted crash (.clsrecord) files.
21+
* The properties these subclasses are nullable on purpose. If there is an issue reading values
22+
* from the crash files, continue as if those fields are optional so a report can still be uploaded.
23+
* That way the issue can potentially be monitored through the backend.
24+
**/
25+
@interface FIRCLSRecordBase : NSObject
26+
27+
/**
28+
* Mark the default initializer as unavailable so the subclasses do not have to add the same line
29+
**/
30+
- (instancetype)init NS_UNAVAILABLE;
31+
32+
/**
33+
* All subclasses should define an initializer taking in a dictionary
34+
**/
35+
- (instancetype)initWithDict:(NSDictionary *)dict;
36+
37+
/**
38+
* Decode hex encoded string
39+
**/
40+
+ (NSString *)decodedHexStringWithValue:(NSString *)hexEncodedString;
41+
42+
@end
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright 2020 Google
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#import "FIRCLSRecordBase.h"
18+
19+
@implementation FIRCLSRecordBase
20+
21+
- (instancetype)initWithDict:(NSDictionary *)dict {
22+
return [super init];
23+
}
24+
25+
+ (NSString *)decodedHexStringWithValue:(NSString *)hexEncodedString {
26+
NSMutableString *decodedString = [[NSMutableString alloc] init];
27+
int index = 0;
28+
while (index < hexEncodedString.length) {
29+
NSString *hexChar = [hexEncodedString substringWithRange:NSMakeRange(index, 2)];
30+
int value = 0;
31+
sscanf([hexChar cStringUsingEncoding:NSASCIIStringEncoding], "%x", &value);
32+
[decodedString appendFormat:@"%c", (char)value];
33+
index += 2;
34+
}
35+
return decodedString;
36+
}
37+
38+
@end
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright 2020 Google
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#import "FIRCLSRecordBase.h"
18+
19+
@interface FIRCLSRecordBinaryImage : FIRCLSRecordBase
20+
21+
@property(nonatomic, copy) NSString *path;
22+
@property(nonatomic, copy) NSString *uuid;
23+
@property(nonatomic, assign) NSUInteger base;
24+
@property(nonatomic, assign) NSUInteger size;
25+
26+
/// Return an array of binary images
27+
/// @param dicts Dictionary describing the binary images
28+
+ (NSArray<FIRCLSRecordBinaryImage *> *)binaryImagesFromDictionaries:
29+
(NSArray<NSDictionary *> *)dicts;
30+
31+
@end
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright 2020 Google
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#import "FIRCLSRecordBinaryImage.h"
18+
19+
@implementation FIRCLSRecordBinaryImage
20+
21+
+ (NSArray<FIRCLSRecordBinaryImage *> *)binaryImagesFromDictionaries:
22+
(NSArray<NSDictionary *> *)dicts {
23+
NSMutableArray<FIRCLSRecordBinaryImage *> *images =
24+
[[NSMutableArray<FIRCLSRecordBinaryImage *> alloc] init];
25+
for (NSDictionary *dict in dicts) {
26+
[images addObject:[[FIRCLSRecordBinaryImage alloc] initWithDict:dict[@"load"]]];
27+
}
28+
return images;
29+
}
30+
31+
- (instancetype)initWithDict:(NSDictionary *)dict {
32+
self = [super initWithDict:dict];
33+
if (self) {
34+
_path = dict[@"path"];
35+
_uuid = dict[@"uuid"];
36+
_base = (NSUInteger)dict[@"base"];
37+
_size = (NSUInteger)dict[@"size"];
38+
}
39+
return self;
40+
}
41+
42+
@end

0 commit comments

Comments
 (0)