Skip to content

Commit

Permalink
fix: even more bugs fixed
Browse files Browse the repository at this point in the history
  • Loading branch information
cvandeplas committed Nov 7, 2024
1 parent 92f8ed6 commit 99c86b9
Show file tree
Hide file tree
Showing 11 changed files with 213 additions and 57 deletions.
4 changes: 3 additions & 1 deletion src/sysdiagnose/parsers/olddsc.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ def get_log_files(self) -> dict:
return log_files

def execute(self) -> list | dict:
return OldDscParser.parse_file(self.get_log_files()[0])
for log_file in self.get_log_files():
return OldDscParser.parse_file(log_file)
return {'error': ['No olddsc files present']}

def parse_file(path: str) -> list | dict:
try:
Expand Down
10 changes: 6 additions & 4 deletions src/sysdiagnose/parsers/ps.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,10 @@ def get_log_files(self) -> list:
return log_files

def execute(self) -> list | dict:
# TODO not really easy to conver to timebased jsonl, as the timestamp is complex to compute.
return PsParser.parse_file(self.get_log_files()[0])
# LATER not really easy to conver to timebased jsonl, as the timestamp is complex to compute.
for log_file in self.get_log_files():
return PsParser.parse_file(log_file)
return {'error': ['No ps.txt file present']}

def parse_file(filename):
result = []
Expand All @@ -63,7 +65,7 @@ def parse_file(filename):
row[col_name] = patterns[col]
result.append(row)
return result
except Exception as e:
except Exception:
logger.exception("Could not parse ps.txt")
return []

Expand Down Expand Up @@ -98,7 +100,7 @@ def export_to_json(processes, filename="./ps.json"):
try:
with open(filename, "w") as fd:
fd.write(json_ps)
except Exception as e:
except Exception:
logger.exception(f"Impossible to dump the processes to {filename}")


Expand Down
16 changes: 11 additions & 5 deletions src/sysdiagnose/parsers/spindumpnosymbols.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,10 @@ def parse_basic(data: list) -> dict:
output[splitted[0]] = splitted[1].strip()

if 'Date/Time' in output:
timestamp = datetime.strptime(output['Date/Time'], "%Y-%m-%d %H:%M:%S.%f %z")
try:
timestamp = datetime.strptime(output['Date/Time'], "%Y-%m-%d %H:%M:%S.%f %z")
except ValueError:
timestamp = datetime.strptime(output['Date/Time'], "%Y-%m-%d %H:%M:%S %z")
output['timestamp'] = timestamp.timestamp()
output['datetime'] = timestamp.isoformat(timespec='microseconds')

Expand Down Expand Up @@ -189,17 +192,20 @@ def parse_thread(data):
priorityregex = re.search(r"priority\ [0-9]+", data[0])
output['priority'] = priorityregex.group(0).split(" ", 1)[1]
if "cpu time" in data[0]:
cputimeregex = re.search(r"cpu\ time\ (.*)\)", data[0])
cputimeregex = re.search(r"cpu\ time\ (.*)", data[0])
output["cputime"] = cputimeregex.group(0).split("time ", 1)[1]

output["loaded"] = []

for line in data[1:]:
loaded = {}
if "+" in line:
loaded["library"] = line.split("(", 1)[1].split("+", 1)[0].strip()
loaded["int"] = line.split("(", 1)[1].split("+", 1)[1].split(")", 1)[0].strip()
loaded["hex"] = line.split("[", 1)[1][:-1].strip()
m = re.search(r"\((?P<library>[^+]+)\+(?P<int>[^\)]+)\) \[(?P<hex>[^\]]+)\](?P<status>.*)", line)
loaded['library'] = m.group('library').strip()
loaded['int'] = m.group('int').strip()
loaded['hex'] = m.group('hex').strip()
if m.group('status').strip() != "":
loaded['status'] = m.group('status').replace('(', '').replace(')', '').strip()
elif "truncated backtrace>" not in line:
loaded["hex"] = line.split("[", 1)[1][:-1].strip()
output["loaded"].append(loaded)
Expand Down
8 changes: 6 additions & 2 deletions src/sysdiagnose/parsers/swcutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import glob
import os
from sysdiagnose.utils.base import BaseParserInterface
from sysdiagnose.utils.base import BaseParserInterface, logger


class SwcutilParser(BaseParserInterface):
Expand All @@ -27,7 +27,11 @@ def get_log_files(self) -> list:
return log_files

def execute(self) -> list | dict:
return SwcutilParser.parse_file(self.get_log_files()[0])
try:
return SwcutilParser.parse_file(self.get_log_files()[0])
except IndexError:
logger.info('No swcutil_show.txt file present.')
return {'error': ['No swcutil_show.txt file present.']}

def parse_file(path: str) -> list | dict:
try:
Expand Down
4 changes: 2 additions & 2 deletions src/sysdiagnose/parsers/taskinfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ def get_log_files(self) -> list:
return log_files

def execute(self) -> list:
path = self.get_log_files()[0]
events = []
try:
path = self.get_log_files()[0]
with open(path, "r") as f:
lines = f.readlines()

Expand Down Expand Up @@ -88,4 +88,4 @@ def execute(self) -> list:
n = n + 1
return events
except IndexError:
return []
return events
99 changes: 58 additions & 41 deletions src/sysdiagnose/parsers/wifiscan.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import glob
import os
import re
from sysdiagnose.utils.base import BaseParserInterface
from sysdiagnose.utils.base import BaseParserInterface, logger


class WifiScanParser(BaseParserInterface):
Expand Down Expand Up @@ -40,51 +40,68 @@ def parse_file(self, path: str) -> list | dict:
parsed_data = {}
# process header
if line.startswith('total='):
items = line.split(',')
for item in items:
key, value = item.split('=')
parsed_data[key.strip()] = value.strip()
parsed_data.update(WifiScanParser.parse_summary(line))
else:
# extract key-value by string

# first ssid and ssid_hex, but need to detect the type first
regexes = [
# iOS 16 and later: FIRSTCONWIFI - ssid=4649525354434f4e57494649
r"(?P<ssid>.+?) - ssid=(?P<ssid_hex>[^,]+)",
# iOS 15: 'FIRSTCONWIFI' (4649525354434f4e57494649)
r"'(?P<ssid>[^\']+)' \((?P<ssid_hex>[^\)]+)\)",
# hidden: <HIDDEN>
r"(?P<ssid><HIDDEN>)(?P<ssid_hex>)",
]
for regex in regexes:
m = re.match(regex, line)
if m:
parsed_data['ssid'] = m.group('ssid')
parsed_data['ssid_hex'] = m.group('ssid_hex')
break
# key = first place with =
# check what is after =, if normal char then value is until next ,
# if [ then value is until ]
# if { then value is until }
index_now = line.index(',') + 1
# now the rest of the line
while index_now < len(line):
index_equals = line.index('=', index_now)
key = line[index_now:index_equals].strip()
if line[index_equals + 1] in ['[']:
index_close = line.index(']', index_now)
value = line[index_equals + 1:index_close].strip()
else:
try:
index_close = line.index(',', index_now)
except ValueError: # catch end of line
index_close = len(line)
value = line[index_equals + 1:index_close].strip()
index_now = index_close + 2
parsed_data[key] = value
parsed_data.update(WifiScanParser.parse_line(line))

timestamp = self.sysdiagnose_creation_datetime
parsed_data['datetime'] = timestamp.isoformat(timespec='microseconds')
parsed_data['timestamp'] = timestamp.timestamp()
output.append(parsed_data)
return output

def parse_summary(line: str) -> dict:
parsed = {}
items = line.split(',')
for item in items:
key, value = item.split('=')
parsed[key.strip()] = value.strip()
return parsed

def parse_line(line: str) -> dict:
parsed = {}
# first ssid and ssid_hex, but need to detect the type first
regexes = [
# iOS 16 and later: FIRSTCONWIFI - ssid=4649525354434f4e57494649
r"(?P<ssid>.+?) - ssid=(?P<ssid_hex>[^,]+)",
# iOS 15: 'FIRSTCONWIFI' (4649525354434f4e57494649)
r"'(?P<ssid>[^\']+)' \((?P<ssid_hex>[^\)]+)\)",
# iOS ??: 'FIRSTCONWIFI' <46495253 54434f4e 57494649>
r"'(?P<ssid>[^\']+)' \<(?P<ssid_hex>[^>]+)\>",
# hidden: <HIDDEN>
r"(?P<ssid><HIDDEN>)(?P<ssid_hex>)",
]
for regex in regexes:
m = re.match(regex, line)
if m:
parsed['ssid'] = m.group('ssid')
parsed['ssid_hex'] = m.group('ssid_hex').replace(' ', '')
break
if 'ssid' not in parsed:
logger.warning(f"Failed to parse ssid from line: {line}")
# key = first place with =
# check what is after =, if normal char then value is until next ,
# if [ then value is until ]
# if { then value is until }
index_now = line.index(',') + 1
# now the rest of the line
while index_now < len(line):
index_equals = line.index('=', index_now)
key = line[index_now:index_equals].strip()
if line[index_equals + 1] in ['[']:
try:
index_close = line.index(']', index_now)
except Exception:
index_close = len(line) # no ending found
value = line[index_equals + 2:index_close].strip()
else:
try:
index_close = line.index(',', index_now)
except ValueError: # catch end of line
index_close = len(line)
value = line[index_equals + 1:index_close].strip()
index_now = index_close + 2
parsed[key] = value

return parsed
4 changes: 3 additions & 1 deletion src/sysdiagnose/parsers/wifisecurity.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ def get_log_files(self) -> list:
return [os.path.join(self.case_data_subfolder, log_files) for log_files in log_files]

def execute(self) -> list | dict:
return WifiSecurityParser.parse_file(self.get_log_files()[0])
for log_file in self.get_log_files():
return WifiSecurityParser.parse_file(log_file)
return {'error': ['No WiFi/security.txt file present']}

def parse_file(path: str) -> list | dict:
"""
Expand Down
4 changes: 4 additions & 0 deletions src/sysdiagnose/utils/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,13 @@ def sysdiagnose_creation_datetime(self) -> datetime:
datetime: The creation date and time of the sysdiagnose.
"""
with open(os.path.join(self.case_data_subfolder, 'sysdiagnose.log'), 'r') as f:
timestamp_regex = None
for line in f:
if 'IN_PROGRESS_sysdiagnose' in line:
timestamp_regex = r"IN_PROGRESS_sysdiagnose_(\d{4}\.\d{2}\.\d{2}_\d{2}-\d{2}-\d{2}[\+,-]\d{4})_"
elif 'spindump_sysdiagnose_' in line:
timestamp_regex = r"spindump_sysdiagnose_(\d{4}\.\d{2}\.\d{2}_\d{2}-\d{2}-\d{2}[\+,-]\d{4})_"
if timestamp_regex:
match = re.search(timestamp_regex, line)
if match:
timestamp = match.group(1)
Expand Down
1 change: 1 addition & 0 deletions src/sysdiagnose/utils/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ def load_plist_file_as_json(fname: str):
except Exception:
return {'error': ['Invalid plist file']}


def load_plist_string_as_json(plist_string: str):
plist = nska_deserialize.deserialize_plist_from_string(plist_string.encode(), full_recurse_convert_nska=True, format=dict)
return json_serializable(plist)
Expand Down
82 changes: 81 additions & 1 deletion tests/test_parsers_spindumpnosymbols.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,28 @@ def test_parse_basic(self):
'Architecture: arm64',
'Report Version: 35.1',
]
expected_result = {'timestamp': 1684960155.759, 'datetime': '2023-05-24T13:29:15.759000-07:00', 'Date/Time': '2023-05-24 13:29:15.759 -0700', 'End time': '2023-05-24 13:29:17.757 -0700', 'OS Version': 'iPhone OS 15.7.6 (Build 19H349)', 'Architecture': 'arm64', 'Report Version': '35.1'}
expected_result = {
'timestamp': 1684960155.759,
'datetime': '2023-05-24T13:29:15.759000-07:00',
'Date/Time': '2023-05-24 13:29:15.759 -0700',
'End time': '2023-05-24 13:29:17.757 -0700',
'OS Version': 'iPhone OS 15.7.6 (Build 19H349)',
'Architecture': 'arm64',
'Report Version': '35.1'
}
result = SpindumpNoSymbolsParser.parse_basic(lines)
self.maxDiff = None
self.assertDictEqual(expected_result, result)

def test_parse_basic_nomili(self):
lines = [
'Date/Time: 2023-05-24 13:29:15 -0700',
]
expected_result = {
'timestamp': 1684960155.000,
'datetime': '2023-05-24T13:29:15.000000-07:00',
'Date/Time': '2023-05-24 13:29:15 -0700'
}
result = SpindumpNoSymbolsParser.parse_basic(lines)
self.maxDiff = None
self.assertDictEqual(expected_result, result)
Expand Down Expand Up @@ -97,6 +118,65 @@ def test_parse_process(self):
self.maxDiff = None
self.assertDictEqual(expected_result, result)

def test_parse_thread(self):
self.maxDiff = None

lines = [
'Thread 0x8b6 DispatchQueue "com.apple.main-thread"(1) 8 samples (1-8) priority 31 (base 31)',
' 8 ??? (dyld + 99536) [0x102c504d0]',
' 8 ??? (accessoryd + 554572) [0x10287b64c]',
' 8 ??? (Foundation + 99872) [0x1821c1620]',
' 8 ??? (Foundation + 97964) [0x1821c0eac]',
' 8 ??? (CoreFoundation + 123252) [0x180ab3174]',
' 8 ??? (CoreFoundation + 44944) [0x180a9ff90]',
' 8 ??? (CoreFoundation + 27784) [0x180a9bc88]',
' 8 ??? (libsystem_kernel.dylib + 2732) [0x1bb3f9aac]',
' *8 ??? [0xfffffff0071a86d4]'
]
expected_result = {
'thread': '0x8b', 'DispatchQueue': 'com.apple.main-thread', 'priority': '31',
'loaded': [
{'library': 'dyld', 'int': '99536', 'hex': '0x102c504d0'},
{'library': 'accessoryd', 'int': '554572', 'hex': '0x10287b64c'},
{'library': 'Foundation', 'int': '99872', 'hex': '0x1821c1620'},
{'library': 'Foundation', 'int': '97964', 'hex': '0x1821c0eac'},
{'library': 'CoreFoundation', 'int': '123252', 'hex': '0x180ab3174'},
{'library': 'CoreFoundation', 'int': '44944', 'hex': '0x180a9ff90'},
{'library': 'CoreFoundation', 'int': '27784', 'hex': '0x180a9bc88'},
{'library': 'libsystem_kernel.dylib', 'int': '2732', 'hex': '0x1bb3f9aac'},
{'hex': '0xfffffff0071a86d4'}
]}
result = SpindumpNoSymbolsParser.parse_thread(lines)
self.assertDictEqual(expected_result, result)

lines = [
'Thread 0x62d DispatchQueue "com.apple.main-thread"(1) 8 samples (1-8) priority 31 (base 31) cpu time 0.005s (4.2M cycles, 1986.9K instructions, 2.14c/i)',
' 8 ??? (dyld + 99536) [0x10236c4d0]',
' 8 ??? (apsd + 281160) [0x10205ca48]'
]
expected_result = {
'thread': '0x62', 'DispatchQueue': 'com.apple.main-thread', 'priority': '31', 'cputime': '0.005s (4.2M cycles, 1986.9K instructions, 2.14c/i)',
'loaded': [
{'library': 'dyld', 'int': '99536', 'hex': '0x10236c4d0'},
{'library': 'apsd', 'int': '281160', 'hex': '0x10205ca48'}
]
}
result = SpindumpNoSymbolsParser.parse_thread(lines)
self.assertDictEqual(expected_result, result)

lines = [
'Thread 0x8477d Thread name "IOConfigThread_\'foobar\'" 1 sample (295) priority 80 (base 80) cpu time <0.001s',
'*1 ??? (kernel + 850132) [0xffffff80002df8d4] (running)'
]
expected_result = {
'thread': '0x84', 'ThreadName': "IOConfigThread_'foobar'", 'priority': '80', 'cputime': '<0.001s',
'loaded': [
{'library': 'kernel', 'int': '850132', 'hex': '0xffffff80002df8d4', 'status': 'running'}
]
}
result = SpindumpNoSymbolsParser.parse_thread(lines)
self.assertDictEqual(expected_result, result)


if __name__ == '__main__':
unittest.main()
Loading

0 comments on commit 99c86b9

Please sign in to comment.