mirror of
				https://github.com/ARM-software/devlib.git
				synced 2025-11-04 07:51:21 +00:00 
			
		
		
		
	Merge pull request #166 from valschneider/logcat-monitor
Logcat monitor
This commit is contained in:
		@@ -14,7 +14,7 @@ from devlib.module import get_module
 | 
			
		||||
from devlib.platform import Platform
 | 
			
		||||
from devlib.exception import TargetError, TargetNotRespondingError, TimeoutError
 | 
			
		||||
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 ABI_MAP, get_cpu_name, ranges_to_list, escape_double_quotes
 | 
			
		||||
from devlib.utils.types import integer, boolean, bitmask, identifier, caseless_string
 | 
			
		||||
@@ -1156,6 +1156,9 @@ class AndroidTarget(Target):
 | 
			
		||||
    def clear_logcat(self):
 | 
			
		||||
        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):
 | 
			
		||||
        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 logging
 | 
			
		||||
import re
 | 
			
		||||
import threading
 | 
			
		||||
import tempfile
 | 
			
		||||
import Queue
 | 
			
		||||
from collections import defaultdict
 | 
			
		||||
 | 
			
		||||
from devlib.exception import TargetError, HostError, DevlibError
 | 
			
		||||
@@ -508,3 +511,129 @@ def _check_env():
 | 
			
		||||
        platform_tools = _env.platform_tools
 | 
			
		||||
        adb = _env.adb
 | 
			
		||||
        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))
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user