mirror of
https://github.com/ARM-software/devlib.git
synced 2025-02-12 07:58:07 +00:00
Merge pull request #166 from valschneider/logcat-monitor
Logcat monitor
This commit is contained in:
commit
0e9221f58e
@ -14,7 +14,7 @@ from devlib.module import get_module
|
|||||||
from devlib.platform import Platform
|
from devlib.platform import Platform
|
||||||
from devlib.exception import TargetError, TargetNotRespondingError, TimeoutError
|
from devlib.exception import TargetError, TargetNotRespondingError, TimeoutError
|
||||||
from devlib.utils.ssh import SshConnection
|
from devlib.utils.ssh import SshConnection
|
||||||
from devlib.utils.android import AdbConnection, AndroidProperties, adb_command, adb_disconnect
|
from devlib.utils.android import AdbConnection, AndroidProperties, LogcatMonitor, adb_command, adb_disconnect
|
||||||
from devlib.utils.misc import memoized, isiterable, convert_new_lines, merge_lists
|
from devlib.utils.misc import memoized, isiterable, convert_new_lines, merge_lists
|
||||||
from devlib.utils.misc import ABI_MAP, get_cpu_name, ranges_to_list, escape_double_quotes
|
from devlib.utils.misc import ABI_MAP, get_cpu_name, ranges_to_list, escape_double_quotes
|
||||||
from devlib.utils.types import integer, boolean, bitmask, identifier, caseless_string
|
from devlib.utils.types import integer, boolean, bitmask, identifier, caseless_string
|
||||||
@ -1156,6 +1156,9 @@ class AndroidTarget(Target):
|
|||||||
def clear_logcat(self):
|
def clear_logcat(self):
|
||||||
adb_command(self.adb_name, 'logcat -c', timeout=30)
|
adb_command(self.adb_name, 'logcat -c', timeout=30)
|
||||||
|
|
||||||
|
def get_logcat_monitor(self, regexps=None):
|
||||||
|
return LogcatMonitor(self, regexps)
|
||||||
|
|
||||||
def adb_kill_server(self, timeout=30):
|
def adb_kill_server(self, timeout=30):
|
||||||
adb_command(self.adb_name, 'kill-server', timeout)
|
adb_command(self.adb_name, 'kill-server', timeout)
|
||||||
|
|
||||||
|
58
devlib/trace/logcat.py
Normal file
58
devlib/trace/logcat.py
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import os
|
||||||
|
import re
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
from devlib.trace import TraceCollector
|
||||||
|
from devlib.utils.android import LogcatMonitor
|
||||||
|
|
||||||
|
class LogcatCollector(TraceCollector):
|
||||||
|
|
||||||
|
def __init__(self, target, regexps=None):
|
||||||
|
super(LogcatCollector, self).__init__(target)
|
||||||
|
self.regexps = regexps
|
||||||
|
self._collecting = False
|
||||||
|
self._prev_log = None
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
"""
|
||||||
|
Clear Collector data but do not interrupt collection
|
||||||
|
"""
|
||||||
|
if not self._monitor:
|
||||||
|
return
|
||||||
|
|
||||||
|
if self._collecting:
|
||||||
|
self._monitor.clear_log()
|
||||||
|
elif self._prev_log:
|
||||||
|
os.remove(self._prev_log)
|
||||||
|
self._prev_log = None
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
"""
|
||||||
|
Start collecting logcat lines
|
||||||
|
"""
|
||||||
|
self._monitor = LogcatMonitor(self.target, self.regexps)
|
||||||
|
if self._prev_log:
|
||||||
|
# Append new data collection to previous collection
|
||||||
|
self._monitor.start(self._prev_log)
|
||||||
|
else:
|
||||||
|
self._monitor.start()
|
||||||
|
|
||||||
|
self._collecting = True
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
"""
|
||||||
|
Stop collecting logcat lines
|
||||||
|
"""
|
||||||
|
if not self._collecting:
|
||||||
|
raise RuntimeError('Logcat monitor not running, nothing to stop')
|
||||||
|
|
||||||
|
self._monitor.stop()
|
||||||
|
self._collecting = False
|
||||||
|
self._prev_log = self._monitor.logfile
|
||||||
|
|
||||||
|
def get_trace(self, outfile):
|
||||||
|
"""
|
||||||
|
Output collected logcat lines to designated file
|
||||||
|
"""
|
||||||
|
# copy self._monitor.logfile to outfile
|
||||||
|
shutil.copy(self._monitor.logfile, outfile)
|
@ -24,6 +24,9 @@ import time
|
|||||||
import subprocess
|
import subprocess
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
|
import threading
|
||||||
|
import tempfile
|
||||||
|
import Queue
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
|
||||||
from devlib.exception import TargetError, HostError, DevlibError
|
from devlib.exception import TargetError, HostError, DevlibError
|
||||||
@ -508,3 +511,129 @@ def _check_env():
|
|||||||
platform_tools = _env.platform_tools
|
platform_tools = _env.platform_tools
|
||||||
adb = _env.adb
|
adb = _env.adb
|
||||||
aapt = _env.aapt
|
aapt = _env.aapt
|
||||||
|
|
||||||
|
class LogcatMonitor(threading.Thread):
|
||||||
|
|
||||||
|
FLUSH_SIZE = 1000
|
||||||
|
|
||||||
|
@property
|
||||||
|
def logfile(self):
|
||||||
|
return self._logfile
|
||||||
|
|
||||||
|
def __init__(self, target, regexps=None):
|
||||||
|
super(LogcatMonitor, self).__init__()
|
||||||
|
|
||||||
|
self.target = target
|
||||||
|
|
||||||
|
self._stopped = threading.Event()
|
||||||
|
self._match_found = threading.Event()
|
||||||
|
|
||||||
|
self._sought = None
|
||||||
|
self._found = None
|
||||||
|
|
||||||
|
self._lines = Queue.Queue()
|
||||||
|
self._datalock = threading.Lock()
|
||||||
|
self._regexps = regexps
|
||||||
|
|
||||||
|
def start(self, outfile=None):
|
||||||
|
if outfile:
|
||||||
|
self._logfile = outfile
|
||||||
|
else:
|
||||||
|
fd, self._logfile = tempfile.mkstemp()
|
||||||
|
os.close(fd)
|
||||||
|
logger.debug('Logging to {}'.format(self._logfile))
|
||||||
|
|
||||||
|
super(LogcatMonitor, self).start()
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.target.clear_logcat()
|
||||||
|
|
||||||
|
logcat_cmd = 'logcat'
|
||||||
|
|
||||||
|
# Join all requested regexps with an 'or'
|
||||||
|
if self._regexps:
|
||||||
|
regexp = '{}'.format('|'.join(self._regexps))
|
||||||
|
if len(self._regexps) > 1:
|
||||||
|
regexp = '({})'.format(regexp)
|
||||||
|
logcat_cmd = '{} -e {}'.format(logcat_cmd, regexp)
|
||||||
|
|
||||||
|
logger.debug('logcat command ="{}"'.format(logcat_cmd))
|
||||||
|
self._logcat = self.target.background(logcat_cmd)
|
||||||
|
|
||||||
|
while not self._stopped.is_set():
|
||||||
|
line = self._logcat.stdout.readline(1024)
|
||||||
|
self._add_line(line)
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
# Popen can be stuck on readline() so send it a SIGKILL
|
||||||
|
self._logcat.terminate()
|
||||||
|
|
||||||
|
self._stopped.set()
|
||||||
|
self.join()
|
||||||
|
|
||||||
|
self._flush_lines()
|
||||||
|
|
||||||
|
def _add_line(self, line):
|
||||||
|
self._lines.put(line)
|
||||||
|
|
||||||
|
if self._sought and re.match(self._sought, line):
|
||||||
|
self._found = line
|
||||||
|
self._match_found.set()
|
||||||
|
|
||||||
|
if self._lines.qsize() >= self.FLUSH_SIZE:
|
||||||
|
self._flush_lines()
|
||||||
|
|
||||||
|
def _flush_lines(self):
|
||||||
|
with self._datalock:
|
||||||
|
with open(self._logfile, 'a') as fh:
|
||||||
|
while not self._lines.empty():
|
||||||
|
fh.write(self._lines.get())
|
||||||
|
|
||||||
|
def clear_log(self):
|
||||||
|
with self._datalock:
|
||||||
|
while not self._lines.empty():
|
||||||
|
self._lines.get()
|
||||||
|
|
||||||
|
with open(self._logfile, 'w') as fh:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_log(self):
|
||||||
|
"""
|
||||||
|
Return the list of lines found by the monitor
|
||||||
|
"""
|
||||||
|
self._flush_lines()
|
||||||
|
|
||||||
|
with self._datalock:
|
||||||
|
with open(self._logfile, 'r') as fh:
|
||||||
|
res = [line for line in fh]
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
def search(self, regexp, timeout=30):
|
||||||
|
"""
|
||||||
|
Search a line that matches a regexp in the logcat log
|
||||||
|
"""
|
||||||
|
res = []
|
||||||
|
|
||||||
|
self._flush_lines()
|
||||||
|
|
||||||
|
with self._datalock:
|
||||||
|
with open(self._logfile, 'r') as fh:
|
||||||
|
for line in fh:
|
||||||
|
if re.match(regexp, line):
|
||||||
|
res.append(line)
|
||||||
|
|
||||||
|
# Found some matches, return them
|
||||||
|
if len(res) > 0:
|
||||||
|
return res
|
||||||
|
|
||||||
|
# Did not find any match, wait for one to pop up
|
||||||
|
self._sought = regexp
|
||||||
|
found = self._match_found.wait(timeout)
|
||||||
|
self._match_found.clear()
|
||||||
|
self._sought = None
|
||||||
|
|
||||||
|
if found:
|
||||||
|
return [self._found]
|
||||||
|
else:
|
||||||
|
raise RuntimeError('Logcat monitor timeout ({}s)'.format(timeout))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user