FileDocCategorySizeDatePackage
EventLogParser.javaAPI DocAndroid 1.5 API20890Wed May 06 22:41:08 BST 2009com.android.ddmlib.log

EventLogParser

public final class EventLogParser extends Object
Parser for the "event" log.

Fields Summary
private static final String
EVENT_TAG_MAP_FILE
Location of the tag map file on the device
private static final int
EVENT_TYPE_INT
Event log entry types. These must match up with the declarations in java/android/android/util/EventLog.java.
private static final int
EVENT_TYPE_LONG
private static final int
EVENT_TYPE_STRING
private static final int
EVENT_TYPE_LIST
private static final Pattern
PATTERN_SIMPLE_TAG
private static final Pattern
PATTERN_TAG_WITH_DESC
private static final Pattern
PATTERN_DESCRIPTION
private static final Pattern
TEXT_LOG_LINE
private final TreeMap
mTagMap
private final TreeMap
mValueDescriptionMap
Constructors Summary
public EventLogParser()

 

      
    
Methods Summary
public java.util.MapgetEventInfoMap()

        return mValueDescriptionMap;
    
private java.lang.ObjectgetObjectFromString(java.lang.String value, com.android.ddmlib.log.EventContainer.EventValueType type)

        try {
            switch (type) {
                case INT:
                    return Integer.valueOf(value);
                case LONG:
                    return Long.valueOf(value);
                case STRING:
                    return value;
            }
        } catch (NumberFormatException e) {
            // do nothing, we'll return null.
        }
        
        return null;
    
public java.util.MapgetTagMap()

        return mTagMap;
    
public booleaninit(com.android.ddmlib.Device device)
Inits the parser for a specific Device.

This methods reads the event-log-tags located on the device to find out what tags are being written to the event log and what their format is.

param
device The device.
return
true if success, false if failure or cancellation.

        // read the event tag map file on the device.
        try {
            device.executeShellCommand("cat " + EVENT_TAG_MAP_FILE, //$NON-NLS-1$
                    new MultiLineReceiver() {
                @Override
                public void processNewLines(String[] lines) {
                    for (String line : lines) {
                        processTagLine(line);
                    }
                }
                public boolean isCancelled() {
                    return false;
                }
            });
        } catch (IOException e) {
            return false;
        }

        return true;
    
public booleaninit(java.lang.String[] tagFileContent)
Inits the parser with the content of a tag file.

param
tagFileContent the lines of a tag file.
return
true if success, false if failure.

        for (String line : tagFileContent) {
            processTagLine(line);
        }
        return true;
    
public booleaninit(java.lang.String filePath)
Inits the parser with a specified event-log-tags file.

param
filePath
return
true if success, false if failure.

        try {
            BufferedReader reader = new BufferedReader(new FileReader(filePath));
            
            String line = null;
            do {
                line = reader.readLine();
                if (line != null) {
                    processTagLine(line);
                }
            } while (line != null);
            
            return true;
        } catch (IOException e) {
            return false;
        }
    
public EventContainerparse(com.android.ddmlib.log.LogReceiver.LogEntry entry)

        if (entry.len < 4) {
            return null;
        }

        int inOffset = 0;

        int tagValue = ArrayHelper.swap32bitFromArray(entry.data, inOffset);
        inOffset += 4;
        
        String tag = mTagMap.get(tagValue);
        if (tag == null) {
            Log.e("EventLogParser", String.format("unknown tag number: %1$d", tagValue));
        }

        ArrayList<Object> list = new ArrayList<Object>();
        if (parseBinaryEvent(entry.data, inOffset, list) == -1) {
            return null;
        }

        Object data;
        if (list.size() == 1) {
            data = list.get(0);
        } else{
            data = list.toArray();
        }

        EventContainer event = null;
        if (tagValue == GcEventContainer.GC_EVENT_TAG) {
            event = new GcEventContainer(entry, tagValue, data);
        } else {
            event = new EventContainer(entry, tagValue, data);
        }
        
        return event;
    
public EventContainerparse(java.lang.String textLogLine)

        // line will look like
        // 04-29 23:16:16.691 I/dvm_gc_info(  427): <data>
        // where <data> is either
        // [value1,value2...]
        // or
        // value
        if (textLogLine.length() == 0) {
            return null;
        }
        
        // parse the header first
        Matcher m = TEXT_LOG_LINE.matcher(textLogLine);
        if (m.matches()) {
            try {
                int month = Integer.parseInt(m.group(1));
                int day = Integer.parseInt(m.group(2));
                int hours = Integer.parseInt(m.group(3));
                int minutes = Integer.parseInt(m.group(4));
                int seconds = Integer.parseInt(m.group(5));
                int milliseconds = Integer.parseInt(m.group(6));
                
                // convert into seconds since epoch and nano-seconds.
                Calendar cal = Calendar.getInstance();
                cal.set(cal.get(Calendar.YEAR), month-1, day, hours, minutes, seconds);
                int sec = (int)Math.floor(cal.getTimeInMillis()/1000);
                int nsec = milliseconds * 1000000;

                String tag = m.group(7);
                
                // get the numerical tag value
                int tagValue = -1;
                Set<Entry<Integer, String>> tagSet = mTagMap.entrySet();
                for (Entry<Integer, String> entry : tagSet) {
                    if (tag.equals(entry.getValue())) {
                        tagValue = entry.getKey();
                        break;
                    }
                }
                
                if (tagValue == -1) {
                    return null;
                }
                
                int pid = Integer.parseInt(m.group(8));
                
                Object data = parseTextData(m.group(9), tagValue);
                if (data == null) {
                    return null;
                }
                
                // now we can allocate and return the EventContainer
                EventContainer event = null;
                if (tagValue == GcEventContainer.GC_EVENT_TAG) {
                    event = new GcEventContainer(tagValue, pid, -1 /* tid */, sec, nsec, data);
                } else {
                    event = new EventContainer(tagValue, pid, -1 /* tid */, sec, nsec, data);
                }
                
                return event;
            } catch (NumberFormatException e) {
                return null;
            }
        }
        
        return null;
    
private static intparseBinaryEvent(byte[] eventData, int dataOffset, java.util.ArrayList list)
Recursively convert binary log data to printable form. This needs to be recursive because you can have lists of lists. If we run out of room, we stop processing immediately. It's important for us to check for space on every output element to avoid producing garbled output. Returns the amount read on success, -1 on failure.


        if (eventData.length - dataOffset < 1)
            return -1;
        
        int offset = dataOffset;

        int type = eventData[offset++];

        //fprintf(stderr, "--- type=%d (rem len=%d)\n", type, eventDataLen);

        switch (type) {
        case EVENT_TYPE_INT: { /* 32-bit signed int */
                int ival;

                if (eventData.length - offset < 4)
                    return -1;
                ival = ArrayHelper.swap32bitFromArray(eventData, offset);
                offset += 4;
                
                list.add(new Integer(ival));
            }
            break;
        case EVENT_TYPE_LONG: { /* 64-bit signed long */
                long lval;

                if (eventData.length - offset < 8)
                    return -1;
                lval = ArrayHelper.swap64bitFromArray(eventData, offset);
                offset += 8;
                
                list.add(new Long(lval));
            }
            break;
        case EVENT_TYPE_STRING: { /* UTF-8 chars, not NULL-terminated */
                int strLen;

                if (eventData.length - offset < 4)
                    return -1;
                strLen = ArrayHelper.swap32bitFromArray(eventData, offset);
                offset += 4;

                if (eventData.length - offset < strLen)
                    return -1;
                
                // get the string
                try {
                    String str = new String(eventData, offset, strLen, "UTF-8"); //$NON-NLS-1$
                    list.add(str);
                } catch (UnsupportedEncodingException e) {
                }
                offset += strLen;
                break;
            }
        case EVENT_TYPE_LIST: { /* N items, all different types */

                if (eventData.length - offset < 1)
                    return -1;

                int count = eventData[offset++];

                // make a new temp list
                ArrayList<Object> subList = new ArrayList<Object>();
                for (int i = 0; i < count; i++) {
                    int result = parseBinaryEvent(eventData, offset, subList);
                    if (result == -1) {
                        return result;
                    }
                    
                    offset += result;
                }

                list.add(subList.toArray());
            }
            break;
        default:
            Log.e("EventLogParser",  //$NON-NLS-1$
                    String.format("Unknown binary event type %1$d", type));  //$NON-NLS-1$
            return -1;
        }
        
        return offset - dataOffset;
    
private java.lang.ObjectparseTextData(java.lang.String data, int tagValue)

        // first, get the description of what we're supposed to parse
        EventValueDescription[] desc = mValueDescriptionMap.get(tagValue);
        
        if (desc == null) {
            // TODO parse and create string values.
            return null;
        }
        
        if (desc.length == 1) {
            return getObjectFromString(data, desc[0].getEventValueType());
        } else if (data.startsWith("[") && data.endsWith("]")) {
            data = data.substring(1, data.length() - 1);
            
            // get each individual values as String
            String[] values = data.split(",");
            
            if (tagValue == GcEventContainer.GC_EVENT_TAG) {
                // special case for the GC event!
                Object[] objects = new Object[2];
                
                objects[0] = getObjectFromString(values[0], EventValueType.LONG);
                objects[1] = getObjectFromString(values[1], EventValueType.LONG);
                
                return objects;
            } else {
                // must be the same number as the number of descriptors.
                if (values.length != desc.length) {
                    return null;
                }
                
                Object[] objects = new Object[values.length];
                
                for (int i = 0 ; i < desc.length ; i++) {
                    Object obj = getObjectFromString(values[i], desc[i].getEventValueType());
                    if (obj == null) {
                        return null;
                    }
                    objects[i] = obj; 
                }
                
                return objects;
            }
        }
        
        return null;
    
private EventValueDescription[]processDescription(java.lang.String description)

        String[] descriptions = description.split("\\s*,\\s*"); //$NON-NLS-1$
        
        ArrayList<EventValueDescription> list = new ArrayList<EventValueDescription>();
        
        for (String desc : descriptions) {
            Matcher m = PATTERN_DESCRIPTION.matcher(desc);
            if (m.matches()) {
                try {
                    String name = m.group(1);

                    String typeString = m.group(2);
                    int typeValue = Integer.parseInt(typeString);
                    EventValueType eventValueType = EventValueType.getEventValueType(typeValue);
                    if (eventValueType == null) {
                        // just ignore this description if the value is not recognized.
                        // TODO: log the error.
                    }
                    
                    typeString = m.group(3);
                    if (typeString != null && typeString.length() > 0) {
                        //skip the |
                        typeString = typeString.substring(1);
                        
                        typeValue = Integer.parseInt(typeString);
                        ValueType valueType = ValueType.getValueType(typeValue);
                        
                        list.add(new EventValueDescription(name, eventValueType, valueType));
                    } else {
                        list.add(new EventValueDescription(name, eventValueType));
                    }
                } catch (NumberFormatException nfe) {
                    // just ignore this description if one number is malformed.
                    // TODO: log the error.
                } catch (InvalidValueTypeException e) {
                    // just ignore this description if data type and data unit don't match
                    // TODO: log the error.
                }
            } else {
                Log.e("EventLogParser",  //$NON-NLS-1$
                    String.format("Can't parse %1$s", description));  //$NON-NLS-1$
            }
        }
        
        if (list.size() == 0) {
            return null;
        }
        
        return list.toArray(new EventValueDescription[list.size()]);
        
    
private voidprocessTagLine(java.lang.String line)
Processes a line from the event-log-tags file.

param
line the line to process

        // ignore empty lines and comment lines
        if (line.length() > 0 && line.charAt(0) != '#") {
            Matcher m = PATTERN_TAG_WITH_DESC.matcher(line);
            if (m.matches()) {
                try {
                    int value = Integer.parseInt(m.group(1));
                    String name = m.group(2);
                    if (name != null && mTagMap.get(value) == null) {
                        mTagMap.put(value, name);
                    }
                    
                    // special case for the GC tag. We ignore what is in the file,
                    // and take what the custom GcEventContainer class tells us.
                    // This is due to the event encoding several values on 2 longs.
                    // @see GcEventContainer
                    if (value == GcEventContainer.GC_EVENT_TAG) {
                        mValueDescriptionMap.put(value,
                            GcEventContainer.getValueDescriptions());
                    } else {
                        
                        String description = m.group(3);
                        if (description != null && description.length() > 0) {
                            EventValueDescription[] desc =
                                processDescription(description);
                            
                            if (desc != null) {
                                mValueDescriptionMap.put(value, desc);
                            }
                        }
                    }
                } catch (NumberFormatException e) {
                    // failed to convert the number into a string. just ignore it.
                }
            } else {
                m = PATTERN_SIMPLE_TAG.matcher(line);
                if (m.matches()) {
                    int value = Integer.parseInt(m.group(1));
                    String name = m.group(2);
                    if (name != null && mTagMap.get(value) == null) {
                        mTagMap.put(value, name);
                    }
                }
            }
        }
    
public voidsaveTags(java.lang.String filePath)
Recreates the event-log-tags at the specified file path.

param
filePath the file path to write the file.
throws
IOException

        File destFile = new File(filePath);
        destFile.createNewFile();
        FileOutputStream fos = null;

        try {
            
            fos = new FileOutputStream(destFile);
            
            for (Integer key : mTagMap.keySet()) {
                // get the tag name
                String tagName = mTagMap.get(key);
                
                // get the value descriptions
                EventValueDescription[] descriptors = mValueDescriptionMap.get(key);
                
                String line = null;
                if (descriptors != null) {
                    StringBuilder sb = new StringBuilder();
                    sb.append(String.format("%1$d %2$s", key, tagName)); //$NON-NLS-1$
                    boolean first = true;
                    for (EventValueDescription evd : descriptors) {
                        if (first) {
                            sb.append(" ("); //$NON-NLS-1$
                            first = false;
                        } else {
                            sb.append(",("); //$NON-NLS-1$
                        }
                        sb.append(evd.getName());
                        sb.append("|"); //$NON-NLS-1$
                        sb.append(evd.getEventValueType().getValue());
                        sb.append("|"); //$NON-NLS-1$
                        sb.append(evd.getValueType().getValue());
                        sb.append("|)"); //$NON-NLS-1$
                    }
                    sb.append("\n"); //$NON-NLS-1$
                    
                    line = sb.toString();
                } else {
                    line = String.format("%1$d %2$s\n", key, tagName); //$NON-NLS-1$
                }
    
                byte[] buffer = line.getBytes();
                fos.write(buffer);
            }
        } finally {
            if (fos != null) {
                fos.close();
            }
        }