Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 83 additions & 14 deletions pidcat.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,15 @@
# Originally written by Jeff Sharkey, http://jsharkey.org/
# Piping detection and popen() added by other Android team members
# Package filtering and output improvements by Jake Wharton, http://jakewharton.com
# Verbose mode, input key listener and time stamps added by Mike Wallace, http://www.risesoftware.com

import argparse
import sys
import re
import subprocess
from subprocess import PIPE
import os
import threading

LOG_LEVELS = 'VDIWEF'
LOG_LEVELS_MAP = dict([(LOG_LEVELS[i], i) for i in range(len(LOG_LEVELS))])
Expand All @@ -41,10 +44,16 @@
parser.add_argument('-c', '--clear', dest='clear_logcat', action='store_true', help='Clear the entire log before running.')
parser.add_argument('-t', '--tag', dest='tag', action='append', help='Filter output by specified tag(s)')
parser.add_argument('-i', '--ignore-tag', dest='ignored_tag', action='append', help='Filter output by ignoring specified tag(s)')
parser.add_argument('--verbose', dest='verbose', action='store_true', help='Shows all logcat lines and some script debub info')

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/debub/debug/


args = parser.parse_args()
min_level = LOG_LEVELS_MAP[args.min_level.upper()]

verbose = args.verbose

if not args.package :
print ("Warning: No package name provided\r")

# Store the names of packages for which to match all processes.
catchall_package = filter(lambda package: package.find(":") == -1, args.package)
# Store the name of processes to match exactly.
Expand Down Expand Up @@ -75,6 +84,41 @@ def termcolor(fg=None, bg=None):
def colorize(message, fg=None, bg=None):
return termcolor(fg, bg) + message + RESET

# Defining the getch() function. Will use msvcrt's in Windows, and raw keystrokes in Linux
try:
from msvcrt import getch
except ImportError:
def getch():
"""Gets a single character from STDIO."""
import sys
import tty
import termios
fd = sys.stdin.fileno()
old = termios.tcgetattr(fd)
try:
tty.setraw(fd)
ch = sys.stdin.read(1)
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old)

return ch

class KeyEventThread(threading.Thread):
def run(self):
while True:
key = getch()
value = ord(key)
if value == 32 or value == 67 or value == 99:
# clear screen on 'c', 'C', or space
os.system("clear")
# Print this line so that the user knows they are still in adb
linebuf = colorize(' ' * (header_size - 1), bg=WHITE)
linebuf += ' Cleared. adb is running...\r'
print(linebuf)
elif ord(key) == 3: # Ctrl-C
adb.terminate()
exit()

def indent_wrap(message):
if width == -1:
return message
Expand All @@ -86,7 +130,7 @@ def indent_wrap(message):
next = min(current + wrap_area, len(message))
messagebuf += message[current:next]
if next < len(message):
messagebuf += '\n'
messagebuf += '\r\n'
messagebuf += ' ' * header_size
current = next
return messagebuf
Expand Down Expand Up @@ -140,12 +184,12 @@ def allocate_color(tag):
'F': colorize(' F ', fg=BLACK, bg=RED),
}

PID_START = re.compile(r'^.*: Start proc ([a-zA-Z0-9._:]+) for ([a-z]+ [^:]+): pid=(\d+) uid=(\d+) gids=(.*)$')
PID_START = re.compile(r'^.*(\d+:\d+:\d+).*: Start proc ([a-zA-Z0-9._:]+) for ([a-z]+ [^:]+): pid=(\d+) uid=(\d+) gids=(.*)$')
PID_START_DALVIK = re.compile(r'^E/dalvikvm\(\s*(\d+)\): >>>>> ([a-zA-Z0-9._:]+) \[ userId:0 \| appId:(\d+) \]$')
PID_KILL = re.compile(r'^Killing (\d+):([a-zA-Z0-9._:]+)/[^:]+: (.*)$')
PID_LEAVE = re.compile(r'^No longer want ([a-zA-Z0-9._:]+) \(pid (\d+)\): .*$')
PID_DEATH = re.compile(r'^Process ([a-zA-Z0-9._:]+) \(pid (\d+)\) has died.?$')
LOG_LINE = re.compile(r'^([A-Z])/(.+?)\( *(\d+)\): (.*?)$')
LOG_LINE = re.compile(r'^.*(\d+:\d+:\d+).* ([A-Z])/(.+?)\( *(\d+)\): (.*?)$')
BUG_LINE = re.compile(r'.*nativeGetEnabledTags.*')
BACKTRACE_LINE = re.compile(r'^#(.*?)pc\s(.*?)$')

Expand All @@ -157,6 +201,12 @@ def allocate_color(tag):
if args.use_emulator:
adb_command.append('-e')
adb_command.append('logcat')
adb_command.append('-v')
adb_command.append('time')

if verbose:
linebuf = "adb_command " + str(adb_command) + "\r"
print (linebuf)

# Clear log before starting logcat
if args.clear_logcat:
Expand All @@ -182,6 +232,13 @@ def poll(self):
last_tag = None
app_pid = None

if verbose:
print ("adb is runnnig...\r")

# Start the thread that checks for keystrokes
keythread = KeyEventThread()
keythread.start()

def match_packages(token):
if len(args.package) == 0:
return True
Expand Down Expand Up @@ -216,8 +273,8 @@ def parse_death(tag, message):
def parse_start_proc(line):
start = PID_START.match(line)
if start is not None:
line_package, target, line_pid, line_uid, line_gids = start.groups()
return line_package, target, line_pid, line_uid, line_gids
log_time, line_package, target, line_pid, line_uid, line_gids = start.groups()
return log_time, line_package, target, line_pid, line_uid, line_gids
start = PID_START_DALVIK.match(line)
if start is not None:
line_pid, line_package, line_uid = start.groups()
Expand All @@ -232,6 +289,10 @@ def parse_start_proc(line):
if len(line) == 0:
break

if verbose:
linebuf = line + "\r"
print (linebuf)

bug_line = BUG_LINE.match(line)
if bug_line is not None:
continue
Expand All @@ -240,31 +301,36 @@ def parse_start_proc(line):
if log_line is None:
continue

level, tag, owner, message = log_line.groups()
log_time, level, tag, owner, message = log_line.groups()
start = parse_start_proc(line)
if start:
line_package, target, line_pid, line_uid, line_gids = start
log_time, line_package, target, line_pid, line_uid, line_gids = start
if match_packages(line_package):
pids.add(line_pid)

app_pid = line_pid

linebuf = '\n'
linebuf = '\r\n'
linebuf += colorize(' ' * (header_size - 1), bg=WHITE)
linebuf += indent_wrap(' Process %s created for %s\r\n' % (line_package, target))
linebuf += colorize(' ' * (header_size - 1), bg=WHITE)
linebuf += indent_wrap(' Process %s created for %s\n' % (line_package, target))
linebuf += ' PID: %s UID: %s GIDs: %s\r\n' % (line_pid, line_uid, line_gids)
linebuf += colorize(' ' * (header_size - 1), bg=WHITE)
linebuf += ' PID: %s UID: %s GIDs: %s' % (line_pid, line_uid, line_gids)
linebuf += '\n'
linebuf += ' Start time: %s' % log_time
linebuf += '\r'
print(linebuf)
last_tag = None # Ensure next log gets a tag printed

dead_pid, dead_pname = parse_death(tag, message)
if dead_pid:
pids.remove(dead_pid)
linebuf = '\n'
linebuf = '\r\n'
linebuf += colorize(' ' * (header_size - 1), bg=RED)
linebuf += ' Process %s (PID: %s) ended' % (dead_pname, dead_pid)
linebuf += '\n'
linebuf += '\r\n'
linebuf += colorize(' ' * (header_size - 1), bg=RED)
linebuf += ' End time: %s' % log_time
linebuf += '\r\n'
print(linebuf)
last_tag = None # Ensure next log gets a tag printed

Expand Down Expand Up @@ -310,4 +376,7 @@ def parse_start_proc(line):
message = matcher.sub(replace, message)

linebuf += indent_wrap(message)
print(linebuf.encode('utf-8'))
linebuf += "\r"

#~ print (linebuf.encode('utf-8'))
print (linebuf)