commit b5326a1cd5bbc0d2ed3d3f74b4905b1185f4ff9b Author: Jake Wharton Date: Tue Jun 11 22:59:50 2013 -0700 Initial import. diff --git a/README.md b/README.md new file mode 100644 index 0000000..670b3d3 --- /dev/null +++ b/README.md @@ -0,0 +1,26 @@ +PID Cat +======= + +An update to Jeff Sharkey's excellent [logcat color script][1] which only shows +log entries for processes from a specific application package. + +During application development you often want to only display log messages +coming from your app. Unfortunately, because the process ID changes every time +you deploy to the phone it becomes a challenge to grep for the right thing. + +This script solves that problem by filtering by application package. Supply the +target pacakge as the sole argument to the python script and enjoy a more +convenient development process. + + ./pidcat.py com.oprah.bees.android + + +Here is an example of the output when running for Twitter and Google Plus + +![Example screen](screen.png) + + + + + + [1]: http://jsharkey.org/blog/2009/04/22/modifying-the-android-logcat-stream-for-full-color-debugging/ \ No newline at end of file diff --git a/pidcat.py b/pidcat.py new file mode 100755 index 0000000..da497c3 --- /dev/null +++ b/pidcat.py @@ -0,0 +1,191 @@ +#!/usr/bin/python + +''' +Copyright 2009, The Android Open Source Project + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +''' + +# Script to highlight adb logcat output for console +# Written by Jeff Sharkey, http://jsharkey.org/ +# Piping detection and popen() added by other Android team members +# Package name restriction by Jake Wharton, http://jakewharton.com + +import os, sys, re, fcntl, termios, struct +from StringIO import StringIO + +if len(sys.argv) != 2: + print 'Target package name required.' + print + print 'Usage: %s com.example.foo' % sys.argv[0] + sys.exit(1) +package = sys.argv[1] + +# unpack the current terminal width/height +data = fcntl.ioctl(sys.stdout.fileno(), termios.TIOCGWINSZ, '1234') +HEIGHT, WIDTH = struct.unpack('hh',data) + +TAG_WIDTH = 22 +HEADER_SIZE = TAG_WIDTH + 1 + 3 + 1 # space, level, space + +BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8) + +def colorize(message, fg=None, bg=None): + ret = '' + codes = [] + if fg is not None: codes.append('3%d' % fg) + if bg is not None: codes.append('10%d' % bg) + if codes: + ret += '\033[%sm' % ';'.join(codes) + ret += message + if codes: + ret += '\033[0m' + return ret + +def indent_wrap(message): + wrap_area = WIDTH - HEADER_SIZE + messagebuf = StringIO() + current = 0 + while current < len(message): + next = min(current + wrap_area, len(message)) + messagebuf.write(message[current:next]) + if next < len(message): + messagebuf.write('\n%s' % (' ' * HEADER_SIZE)) + current = next + return messagebuf.getvalue() + + +LAST_USED = [RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN] +KNOWN_TAGS = { + 'dalvikvm': WHITE, + 'Process': WHITE, + 'ActivityManager': WHITE, + 'ActivityThread': WHITE, + 'AndroidRuntime': CYAN, + 'jdwp': WHITE, +} + +def allocate_color(tag): + # this will allocate a unique format for the given tag + # since we dont have very many colors, we always keep track of the LRU + if tag not in KNOWN_TAGS: + KNOWN_TAGS[tag] = LAST_USED[0] + color = KNOWN_TAGS[tag] + if color in LAST_USED: + LAST_USED.remove(color) + LAST_USED.append(color) + return color + + +RULES = { + #re.compile(r"([\w\.@]+)=([\w\.@]+)"): r"%s\1%s=%s\2%s" % (format(fg=BLUE), format(fg=GREEN), format(fg=BLUE), format(reset=True)), +} + +TAGTYPES = { + 'V': colorize(' V ', fg=WHITE, bg=BLACK), + 'D': colorize(' D ', fg=BLACK, bg=BLUE), + 'I': colorize(' I ', fg=BLACK, bg=GREEN), + 'W': colorize(' W ', fg=BLACK, bg=YELLOW), + 'E': colorize(' E ', fg=BLACK, bg=RED), +} + +PID_START = re.compile(r'^Start proc ([a-zA-Z0-9._]+) for ([a-z]+ [^:]+): pid=(\d+) uid=(\d+) gids=(.*)\r?$') +PID_KILL = re.compile(r'^Killing (\d+):([a-zA-Z0-9._]+)/[^:]+: (.*)\r?$') +PID_DEATH = re.compile(r'^No longer want ([a-zA-Z0-9._]+) \(pid (\d+)\): .*\r?$') +LOG_LINE = re.compile(r'^([A-Z])/([^\(]+)\( *(\d+)\): (.*)\r?$') + +input = os.popen('adb logcat') +pid = None + +while True: + try: + line = input.readline() + except KeyboardInterrupt: + break + + log_line = LOG_LINE.match(line) + if not log_line is None: + level, tag, owner, message = log_line.groups() + + start = PID_START.match(message) + if start is not None: + line_package, target, line_pid, line_uid, line_gids = start.groups() + + if line_package == package: + pid = line_pid + + linebuf = StringIO() + linebuf.write('\n' * 3) + linebuf.write(colorize(' ' * (HEADER_SIZE - 1), bg=WHITE)) + linebuf.write(' Starting for %s\n' % target) + linebuf.write(colorize(' ' * (HEADER_SIZE - 1), bg=WHITE)) + linebuf.write(' PID: %s UID: %s GIDs: %s' % (line_pid, line_uid, line_gids)) + linebuf.write('\n') + + print linebuf.getvalue() + + kill = PID_KILL.match(message) + if kill is not None: + line_pid, line_package, reason = kill.groups() + if 'ActivityManager' == tag and pid == line_pid and package == line_package: + pid = None + + linebuf = StringIO() + linebuf.write('\n') + linebuf.write(colorize(' ' * (HEADER_SIZE - 1), bg=RED)) + linebuf.write(' Killing because %s' % reason) + linebuf.write('\n' * 3) + + print linebuf.getvalue() + + death = PID_DEATH.match(message) + if death is not None: + line_package, line_pid = death.groups() + if 'ActivityManager' == tag and pid == line_pid and package == line_package: + pid = None + + linebuf = StringIO() + linebuf.write('\n') + linebuf.write(colorize(' ' * (HEADER_SIZE - 1), bg=RED)) + linebuf.write(' No longer wanted') + linebuf.write('\n' * 3) + + print linebuf.getvalue() + + if pid is None or owner != pid: + continue + + linebuf = StringIO() + + # right-align tag title and allocate color if needed + tag = tag.strip() + color = allocate_color(tag) + tag = tag[-TAG_WIDTH:].rjust(TAG_WIDTH) + linebuf.write(colorize(tag, fg=color)) + linebuf.write(' ') + + # write out level colored edge + if level not in TAGTYPES: break + linebuf.write(TAGTYPES[level]) + linebuf.write(' ') + + # format tag message using rules + for matcher in RULES: + replace = RULES[matcher] + message = matcher.sub(replace, message) + + linebuf.write(indent_wrap(message)) + line = linebuf.getvalue() + + print line + if len(line) == 0: break diff --git a/screen.png b/screen.png new file mode 100644 index 0000000..8c0cc02 Binary files /dev/null and b/screen.png differ