├── coloredlogcat.py ├── dexdumpmethods.sh ├── pickdevice.pl └── proclogcat /coloredlogcat.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | ''' 4 | Copyright 2009, The Android Open Source Project 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | ''' 18 | 19 | # script to highlight adb logcat output for console 20 | # written by jeff sharkey, http://jsharkey.org/ 21 | # piping detection and popen() added by other android team members 22 | 23 | # modified by Josh Guilfoyle to simplify formatting back to 24 | # adb logcat original. I just want colorized logcat output, nothing fancier. 25 | 26 | import os, sys, re, StringIO 27 | import fcntl, termios, struct 28 | 29 | BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8) 30 | 31 | def format(fg=None, bg=None, bright=False, bold=False, dim=False, reset=False): 32 | # manually derived from http://en.wikipedia.org/wiki/ANSI_escape_code#Codes 33 | codes = [] 34 | if reset: codes.append("0") 35 | else: 36 | if not fg is None: codes.append("3%d" % (fg)) 37 | if not bg is None: 38 | if not bright: codes.append("4%d" % (bg)) 39 | else: codes.append("10%d" % (bg)) 40 | if bold: codes.append("1") 41 | elif dim: codes.append("2") 42 | else: codes.append("22") 43 | return "\033[%sm" % (";".join(codes)) 44 | 45 | def colorToFormat(color): 46 | return format(fg=color, bold=True) 47 | 48 | LAST_USED = map(colorToFormat, [RED,GREEN,YELLOW,BLUE,MAGENTA,CYAN,WHITE]) 49 | KNOWN_TAGS = { 50 | "dalvikvm": format(fg=BLACK, bold=True), 51 | "Process": format(fg=YELLOW, bold=True), 52 | "ActivityManager": format(fg=CYAN, bold=True), 53 | "ActivityThread": format(fg=CYAN, bold=True), 54 | "System.err": format(bg=BLUE), 55 | "System.out": format(bg=BLUE), 56 | } 57 | 58 | def allocate_color(tag): 59 | # this will allocate a unique format for the given tag 60 | # since we dont have very many colors, we always keep track of the LRU 61 | if not tag in KNOWN_TAGS: 62 | KNOWN_TAGS[tag] = LAST_USED[0] 63 | color = KNOWN_TAGS[tag] 64 | if color in LAST_USED: 65 | LAST_USED.remove(color) 66 | LAST_USED.append(color) 67 | return color 68 | 69 | 70 | RULES = { 71 | #re.compile(r"([\w\.@]+)=([\w\.@]+)"): r"%s\1%s=%s\2%s" % (format(fg=BLUE), format(fg=GREEN), format(fg=BLUE), format(reset=True)), 72 | } 73 | 74 | TAGTYPE_WIDTH = 1 75 | TAG_WIDTH = 20 76 | PROCESS_WIDTH = 8 # 8 or -1 77 | HEADER_SIZE = TAGTYPE_WIDTH + 1 + TAG_WIDTH + 1 + PROCESS_WIDTH + 1 78 | 79 | TAGTYPES = { 80 | "V": "", 81 | "D": format(fg=BLUE, bold=True), 82 | "I": "", 83 | "W": format(fg=YELLOW, bold=True), 84 | "E": format(fg=RED, bold=True), 85 | } 86 | 87 | retag = re.compile("^([A-Z])/(.+?)\((\s*\d+)\): (.*)$") 88 | 89 | # to pick up -d or -e 90 | adb_args = ' '.join(sys.argv[1:]) 91 | 92 | # if someone is piping in to us, use stdin as input. if not, invoke adb logcat 93 | if os.isatty(sys.stdin.fileno()): 94 | input = os.popen("adb %s logcat" % adb_args) 95 | else: 96 | input = sys.stdin 97 | 98 | while True: 99 | try: 100 | line = input.readline().rstrip() 101 | except KeyboardInterrupt: 102 | break 103 | 104 | match = retag.match(line) 105 | if not match is None: 106 | tagtype, tag, owner, message = match.groups() 107 | linebuf = StringIO.StringIO() 108 | 109 | # write out tagtype colored edge 110 | if not tagtype in TAGTYPES: break 111 | linebuf.write("%s%s%s" % (TAGTYPES[tagtype], tagtype, format(reset=True))) 112 | 113 | colorfmt = allocate_color(tag) 114 | linebuf.write(" / %s%s%s" % (colorfmt, tag, format(reset=True))) 115 | 116 | linebuf.write(" (%s): " % owner) 117 | 118 | # format tag message using rules 119 | for matcher in RULES: 120 | replace = RULES[matcher] 121 | message = matcher.sub(replace, message) 122 | 123 | linebuf.write("%s%s%s" % (colorfmt, message, format(reset=True))) 124 | line = linebuf.getvalue() 125 | 126 | print line 127 | if len(line) == 0: break 128 | -------------------------------------------------------------------------------- /dexdumpmethods.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ############################################################################### 3 | ## 4 | ## Dump a list of methods from an apk or dx'd jar file. This script was 5 | ## originally constructed to explore the general platform size of various 6 | ## framework jar files, but may be useful to others in light of the discovered 7 | ## Dalvik limit on total number of permitted methods as discussed here: 8 | ## 9 | ## http://code.google.com/p/android/issues/detail?id=20814 10 | ## 11 | ## Usage: 12 | ## 13 | ## dexdumpmethods.sh foo.apk 14 | ## 15 | ## I typically pipe the output to 'wc -l' to get a rough sense of size. 16 | ## 17 | ############################################################################### 18 | 19 | filename=$1 20 | 21 | # Dumps the method information to XML through the standard dexdump tool then 22 | # processes it with XPath. Basically, this selects all method tags, prints the 23 | # class name then the method, and then iterates through all child parameters and 24 | # formats them appropriately. Most of the complexity is just getting the 25 | # formatting looking right :) 26 | dexdump -l xml "$filename" | \ 27 | xmlstarlet sel -t \ 28 | -m '//class/method' -v '../@name' -o '#' -v '@name' -o '(' \ 29 | -m 'parameter' -o '' -v '@type' \ 30 | -i 'not(position()=last())' -o ', ' \ 31 | -b -b -o ')' -i 'not(position()=last())' -n -b 32 | -------------------------------------------------------------------------------- /pickdevice.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | ############################################################################### 3 | ## 4 | ## Easily manipulate the value of ANDROID_SERIAL by presenting an interactive 5 | ## list of currently connected devices. 6 | ## 7 | ## Designed to be invoked by the shell as: 8 | ## 9 | ## eval $(pickdevice) 10 | ## 11 | ############################################################################### 12 | 13 | use strict; 14 | use Data::Dumper; 15 | 16 | ############################################################################### 17 | 18 | my @devicesOutput = qx{adb devices}; 19 | shift(@devicesOutput) =~ m/^List of devices/i or 20 | die "Unexpected `adb devices' output.\n"; 21 | 22 | my @devices; 23 | my $defaultChoice; 24 | 25 | foreach (@devicesOutput) { 26 | chomp; 27 | next if m/^\s*$/; 28 | 29 | my ($serial, $desc) = split /\s+/, $_, 2; 30 | 31 | push @devices, { serial => $serial, desc => $desc }; 32 | 33 | if (!defined($defaultChoice) && $serial !~ m/^emulator/) { 34 | $defaultChoice = $#devices; 35 | } 36 | } 37 | 38 | if (@devices == 0) { 39 | die "No devices connected?\n"; 40 | } 41 | 42 | if (!defined($defaultChoice)) { 43 | $defaultChoice = 0; 44 | } 45 | 46 | print STDERR "Pick a device:\n"; 47 | 48 | for (0 .. $#devices) { 49 | print STDERR "\t", $_, ". ", $devices[$_]->{serial}, "\n"; 50 | } 51 | 52 | print STDERR "\n"; 53 | print STDERR "Which would you like for ANDROID_SERIAL? [$defaultChoice] "; 54 | chomp (my $answer = ); 55 | 56 | if (length $answer == 0) { 57 | $answer = $defaultChoice; 58 | } 59 | 60 | print "export ANDROID_SERIAL=", $devices[$answer]->{serial}, "\n"; 61 | -------------------------------------------------------------------------------- /proclogcat: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | ############################################################################### 3 | ## 4 | ## Simple script designed to insert after "adb logcat" in a pipeline to track a 5 | ## specific package's logcat messages. 6 | ## 7 | ## I typically invoke this script as a function in my profile: 8 | ## 9 | ## function plogcat() { 10 | ## adb logcat | proclogcat $* | coloredlogcat.py 11 | ## } 12 | ## 13 | ## Then run as: 14 | ## 15 | ## $ plogcat org.devtcg.five 16 | ## 17 | ############################################################################### 18 | 19 | use strict; 20 | use Data::Dumper; 21 | 22 | ############################################################################### 23 | 24 | @ARGV > 0 or usage($0); 25 | 26 | # We support tracking multiple process names. Fill %trackingInfo keys with the 27 | # process names, where the values are to be filled with the current pid for 28 | # that process or -1 if it is presumed dead. 29 | my $trackingInfo = { map { $_ => -1 } @ARGV }; 30 | 31 | # Flush all writes immediately. This is necessary as we expect this script to 32 | # be placed between two other programs in a pipe which outputs text very slowly 33 | # (adb logcat outputs only when events happen), so it's rare to fill up the 34 | # buffer quickly. Without this, the normal buffering that occurs between piped 35 | # programs not directly attached to a pty would prevent the user from seeing 36 | # messages as they arrive. 37 | $| = 1; 38 | 39 | # Lookup the pids of the processes before we start. From then on, rely on 40 | # the ActivityManager to tell us as the processes dies and starts. 41 | my $numPids = get_pids($trackingInfo); 42 | if ($numPids == 0) { 43 | print "- waiting for process ", join(' or ', keys %$trackingInfo), " -\n"; 44 | } 45 | 46 | while () { 47 | my $line = $_; 48 | my ($level, $tag, $pid, $message) = $line =~ 49 | m/^([A-Z])\/(.*?)\(\s*(\d+)\s*\): (.*)$/; 50 | 51 | chomp $message; 52 | 53 | if ($tag eq 'ActivityManager') { 54 | if ($message =~ m/^Start proc (.*?) .*?: pid=(\d+) /) { 55 | if (exists $trackingInfo->{$1}) { 56 | $trackingInfo->{$1} = $2; 57 | print $line; 58 | } 59 | } elsif ($message =~ m/Start proc (\d+):(.*?)\//) { 60 | if (exists $trackingInfo->{$2}) { 61 | $trackingInfo->{$2} = $1; 62 | print $line; 63 | } 64 | } elsif ($message =~ m/Process (.*?) \(pid (\d+)\) has died./) { 65 | if (exists $trackingInfo->{$1}) { 66 | $trackingInfo->{$1} = -1; 67 | print $line; 68 | } 69 | } 70 | } elsif (in_list($pid, values %$trackingInfo)) { 71 | print $line; 72 | } 73 | } 74 | 75 | ############################################################################### 76 | 77 | sub in_list($@) { 78 | my $needle = shift; 79 | my @haystack = @_; 80 | 81 | foreach my $hay (@haystack) { 82 | if ($hay eq $needle) { 83 | return 1; 84 | } 85 | } 86 | return 0; 87 | } 88 | 89 | sub get_pids { 90 | my $info = shift; 91 | my @ps = qx{adb shell ps}; 92 | if (@ps == 0) { 93 | return -1; 94 | } 95 | my @columns = split /\s+/, (shift @ps); 96 | 97 | # There's a "STATE" column slipped in between WCHAN and NAME that has no 98 | # room for a column... 99 | splice @columns, $#columns, 0, 'STATE'; 100 | 101 | my $numFound = 0; 102 | 103 | foreach (@ps) { 104 | s/\s+$//; 105 | my @data = split /\s+/, $_, scalar @columns; 106 | my %row = map { $_ => (shift @data) } @columns; 107 | 108 | if (exists $info->{$row{NAME}}) { 109 | $info->{$row{NAME}} = $row{PID}; 110 | $numFound++; 111 | } 112 | } 113 | 114 | return $numFound; 115 | } 116 | 117 | sub usage { 118 | my $prog = shift; 119 | die <<"EOF" 120 | Usage: adb logcat | $0 121 | 122 | Usually, `process-name' is usually the same as your package, but not 123 | necessarily. To make sure, type `adb shell ps' and look through the list. 124 | EOF 125 | } 126 | 127 | --------------------------------------------------------------------------------