diff --git a/README.md b/README.md new file mode 100644 index 0000000..6847fa9 --- /dev/null +++ b/README.md @@ -0,0 +1,26 @@ +What is this? +------------- + +This is a set of scripts to enhance your development time with adb. + +* LOGCAT: + +`./super-logcat.sh ` + +So, if your process name is `com.example.myapp`, then running `./super-logcat.sh com.example` will show all messages related to your application. + +Running the `super-logcat.sh` script with no arguments will display the logcat output from every process, piped through the same formatting and coloring algorithm. + + +* DEVICES: + +`./pickdevice.pl` + +This will help you easily select the device to run the commands on. This is usefull when using multiple devices/emulators at the same time. + + +* METHOD_ANALYSING + +`./dexdumpmethods.sh ` + +You will see all methods in this package. It is usefull to pipe it into `wc -l` to see an overall number of functions. diff --git a/coloredlogcat.py b/coloredlogcat.py index 856f314..04213a6 100755 --- a/coloredlogcat.py +++ b/coloredlogcat.py @@ -48,6 +48,7 @@ def colorToFormat(color): LAST_USED = map(colorToFormat, [RED,GREEN,YELLOW,BLUE,MAGENTA,CYAN,WHITE]) KNOWN_TAGS = { "dalvikvm": format(fg=BLACK, bold=True), + "AndroidRuntime": format(fg=MAGENTA, bold=True), "Process": format(fg=YELLOW, bold=True), "ActivityManager": format(fg=CYAN, bold=True), "ActivityThread": format(fg=CYAN, bold=True), @@ -67,15 +68,6 @@ def allocate_color(tag): 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)), -} - -TAGTYPE_WIDTH = 1 -TAG_WIDTH = 20 -PROCESS_WIDTH = 8 # 8 or -1 -HEADER_SIZE = TAGTYPE_WIDTH + 1 + TAG_WIDTH + 1 + PROCESS_WIDTH + 1 - TAGTYPES = { "V": "", "D": format(fg=BLUE, bold=True), @@ -84,7 +76,17 @@ def allocate_color(tag): "E": format(fg=RED, bold=True), } + +TAGTYPENAMES = { + "V": "Verb ", + "D": "Debug", + "I": "Info ", + "W": "WARN ", + "E": "ERROR" +} + retag = re.compile("^([A-Z])/(.+?)\((\s*\d+)\): (.*)$") +newRetag = re.compile("^([0-9-\s:.]+?)\s+(\d+)\s+(\d+)\s+([A-Z])\s+(.*?)\s*: (.*)$") # to pick up -d or -e adb_args = ' '.join(sys.argv[1:]) @@ -100,28 +102,39 @@ def allocate_color(tag): line = input.readline().rstrip() except KeyboardInterrupt: break + if len(line) == 0: break match = retag.match(line) + newMatch = newRetag.match(line) if not match is None: tagtype, tag, owner, message = match.groups() linebuf = StringIO.StringIO() # write out tagtype colored edge if not tagtype in TAGTYPES: break - linebuf.write("%s%s%s" % (TAGTYPES[tagtype], tagtype, format(reset=True))) + linebuf.write("%s%s%s" % (TAGTYPES[tagtype], TAGTYPENAMES[tagtype], format(reset=True))) colorfmt = allocate_color(tag) linebuf.write(" / %s%s%s" % (colorfmt, tag, format(reset=True))) linebuf.write(" (%s): " % owner) - # format tag message using rules - for matcher in RULES: - replace = RULES[matcher] - message = matcher.sub(replace, message) - linebuf.write("%s%s%s" % (colorfmt, message, format(reset=True))) line = linebuf.getvalue() + elif not newMatch is None: + time, owner, ppid, tagtype, tag, message = newMatch.groups() + if not tagtype in TAGTYPES: break + linebuf = StringIO.StringIO() + linebuf.write("%s %05s | %05s" % (time, owner, ppid)) + + + # write out tagtype colored edge + linebuf.write(" %s%s%s" % (TAGTYPES[tagtype], TAGTYPENAMES[tagtype], format(reset=True))) + + colorfmt = allocate_color(tag) + linebuf.write(" / %s%s%s: " % (colorfmt, tag, format(reset=True))) + + linebuf.write("%s%s%s" % (colorfmt, message, format(reset=True))) + line = linebuf.getvalue() print line - if len(line) == 0: break diff --git a/proclogcat b/proclogcat index 5266803..f558eff 100755 --- a/proclogcat +++ b/proclogcat @@ -21,12 +21,13 @@ use Data::Dumper; ############################################################################### -@ARGV > 0 or usage($0); +@ARGV > 0 and not -t STDIN or usage($0); -# We support tracking multiple process names. Fill %trackingInfo keys with the -# process names, where the values are to be filled with the current pid for -# that process or -1 if it is presumed dead. -my $trackingInfo = { map { $_ => -1 } @ARGV }; +# We support tracking multiple process names. Fill @trackedNames with the +# process names to match (via regular expressions). The process IDs that we +# are currently tracking will be stored in %trackedPids. +my @trackedNames = @ARGV; +my %trackedPids = (); # Flush all writes immediately. This is necessary as we expect this script to # be placed between two other programs in a pipe which outputs text very slowly @@ -38,36 +39,51 @@ $| = 1; # Lookup the pids of the processes before we start. From then on, rely on # the ActivityManager to tell us as the processes dies and starts. -my $numPids = get_pids($trackingInfo); +my $numPids = get_pids(); if ($numPids == 0) { - print "- waiting for process ", join(' or ', keys %$trackingInfo), " -\n"; + print "- waiting for process ", join(' or ', @trackedNames), " -\n"; } while () { my $line = $_; - my ($level, $tag, $pid, $message) = $line =~ + my ($time, $pid, $ppid, $level, $tag, $message) = $line =~ + m/^([0-9-\s:.]+)\s+(\d+)\s+(\d+)\s+([A-Z])\s+(.*?)\s*: (.*)$/; + if ($pid == 0) { + ($level, $tag, $pid, $message) = $line =~ m/^([A-Z])\/(.*?)\(\s*(\d+)\s*\): (.*)$/; + } + + # print line if does not match the regex + if ($pid == 0 && $tag == 0) { + print $line; + next; + } chomp $message; if ($tag eq 'ActivityManager') { if ($message =~ m/^Start proc (.*?) .*?: pid=(\d+) /) { - if (exists $trackingInfo->{$1}) { - $trackingInfo->{$1} = $2; + if (match_name($1)) { + $trackedPids{$2} = 1; print $line; } } elsif ($message =~ m/Start proc (\d+):(.*?)\//) { - if (exists $trackingInfo->{$2}) { - $trackingInfo->{$2} = $1; + if (match_name($2)) { + $trackedPids{$1} = 1; print $line; } } elsif ($message =~ m/Process (.*?) \(pid (\d+)\) has died./) { - if (exists $trackingInfo->{$1}) { - $trackingInfo->{$1} = -1; + if (match_name($1)) { + delete $trackedPids{$2}; + print $line; + } + } elsif ($message =~ m/Killing (\d+):(.*?)\//) { + if (match_name($2)) { + delete $trackedPids{$1}; print $line; } } - } elsif (in_list($pid, values %$trackingInfo)) { + } elsif ($trackedPids{$pid} || in_list($tag, @trackedNames)) { print $line; } } @@ -87,16 +103,17 @@ sub in_list($@) { } sub get_pids { - my $info = shift; - my @ps = qx{adb shell ps}; + my @ps = qx{adb wait-for-device && adb shell ps}; if (@ps == 0) { return -1; } my @columns = split /\s+/, (shift @ps); - # There's a "STATE" column slipped in between WCHAN and NAME that has no - # room for a column... - splice @columns, $#columns, 0, 'STATE'; + if (! in_list("S", @columns)) { + # There's a "STATE" column slipped in between WCHAN and NAME that has no + # room for a column name... + splice @columns, $#columns, 0, 'STATE'; + } my $numFound = 0; @@ -105,8 +122,8 @@ sub get_pids { my @data = split /\s+/, $_, scalar @columns; my %row = map { $_ => (shift @data) } @columns; - if (exists $info->{$row{NAME}}) { - $info->{$row{NAME}} = $row{PID}; + if (match_name($row{NAME})) { + $trackedPids{$row{PID}} = 1; $numFound++; } } @@ -114,10 +131,22 @@ sub get_pids { return $numFound; } +sub match_name { + my $name = shift; + foreach (@trackedNames) { + return 1 if $_ and $name =~ $_; + } + return 0; +} + sub usage { my $prog = shift; die <<"EOF" -Usage: adb logcat | $0 +Usage: adb logcat | $0 [ ...] + +Process names will be matched against the given regular expression(s), +and only the matching processes will be shown in the output. +Or the tag is equals to the it and it will be shown. Usually, `process-name' is usually the same as your package, but not necessarily. To make sure, type `adb shell ps' and look through the list. diff --git a/super-logcat.sh b/super-logcat.sh new file mode 100755 index 0000000..9465886 --- /dev/null +++ b/super-logcat.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +if [ ! "$*" ]; then + "$0" ".+" + exit $? +fi + +dir=`dirname "$0"` + +if [ "`readlink $0`" ]; then + dir=`dirname "$(readlink $0)"` +fi + +adb logcat | "$dir/proclogcat" "$@" | "$dir/coloredlogcat.py"