blob: af725abf9f0aa048598205474ec0c2126989fba2 [file] [log] [blame]
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright 2018 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# Extracts memd logs from feedback report logs, and reproduces the original
# /var/log/memd directory, containing the file memd.parameters and one or more
# memd.clip{000,001,...}.log (the "clip" files) with the sampled data. This is
# the format expected by
"""Extracts memd logs from feedback report logs."""
from __future__ import print_function
import argparse
import glob
import os
import re
import subprocess
import sys
def die(message):
"""Prints message and exits with failure status."""
print('', message)
class Extractor(object):
"""Methods to reconstruct memd logs from a feedback report."""
def __init__(self, args):
self._args = args
self._timestamp_re = re.compile(r'\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d\..*')
def setup_outdir(self, outdir):
"""Sets up the output directory by creating or cleaning it as needed."""
if not os.path.exists(outdir):
if not os.path.isdir(outdir):
die('output directory path %s exists but is not a directory' % outdir)
files = glob.glob('%s/*' % outdir)
clip_re = re.compile(r'^%s/*memd.clip\d\d\d\.log$' % outdir)
for filename in files:
if filename == 'memd.parameters' or clip_re.match(filename):
def unzip_and_open(self, filename):
"""Unzips |filename| and opens system_logs.txt.
Ensures that |filename| is a ZIP archive containing system_logs.txt,
unzips it, and opens it.
list_output = subprocess.check_output(['unzip', '-l', filename])
for line in list_output.splitlines():
if re.match(r'.*system_logs.txt$', str(line, encoding='utf-8')):
subprocess.check_call(['unzip', '-o', filename])
return open('system_logs.txt', 'r')
die('%s does not contain system_logs.txt' % filename)
def extract_sections(self, filename):
"""Extracts memd-related sections of the feedback logs in |filename|.
The contents of |filename| are expected to be in feedback log format.
Opens |filename| (if needed, |filename| is first uncompressed into
system_logs.txt) and returns the content of memd parameters and clip
files from the feedback log. The clips are expected to appear first (as
per alphabetical order of the filenames).
if re.match(r'^.*\.zip', filename):
input_file = self.unzip_and_open(filename)
input_file = open(filename, 'r')
# |memd_parameters| is an array of lines.
memd_parameters = []
# |memd_clip_lines| is an array of lines.
memd_clip_lines = []
scan_state_start, scan_state_clips, scan_state_parameters = range(3)
scan_state = scan_state_start
for line in input_file:
# In the START state look for beginning of sections.
if scan_state == scan_state_start:
if line.startswith('memd clips=<multiline>'):
scan_state = scan_state_clips
if line.startswith('memd.parameters=<multiline>'):
scan_state = scan_state_parameters
if scan_state == scan_state_clips:
if '--- END ---' in line:
scan_state = scan_state_start
if scan_state == scan_state_parameters:
if '--- END ---' in line:
scan_state = scan_state_start
# Assume memd.parameters comes after memd clips, thus we're done.
if scan_state != scan_state_start:
die('missing END line in multiline section')
if len(memd_clip_lines) == 0:
die('missing memd_clips section')
if '--- START ---' not in memd_clip_lines[0]:
die('missing START line in memd clips section')
if '--- START ---' not in memd_parameters[0]:
die('missing START line in memd.parameters section')
memd_clips = self.extract_clips(memd_clip_lines[1:])
return (memd_parameters[1:], memd_clips)
def extract_clips(self, clip_lines):
"""Extracts the content of clip files.
|clip_lines| contains lines from a feedback report, where all clip files
are concatenated and need to be separated by looking at their two-line
header. Returns an array of array of lines, each element representing the
content of a clip file.
clips = []
this_clip = []
for line in clip_lines:
if self._timestamp_re.match(line):
this_clip = []
return clips[1:]
def run(self):
"""Extracts memd parameters and clip files from a feedback report log.
The log may be compressed (*.zip) or plain text (all other file names).
The output files are placed in the specified output directory.
outdir = self._args['outdir']
(parameters, clips) = self.extract_sections(self._args['input-file'])
with open('%s/memd.parameters' % outdir, 'w') as f:
for line in parameters:
clip_number = 0
for clip in clips:
with open('%s/memd.clip%03d.log' % (outdir, clip_number), 'w') as f:
clip_number += 1
for line in clip:
def main():
"""Extracts memd logs from feedback report logs."""
parser = argparse.ArgumentParser(
description='Extract memd logs from feedback report logs.')
parser.add_argument('-o', '--outdir', default='memd')
extractor = Extractor(vars(parser.parse_args()))