OSDN Git Service

More robust parsing for smaps
[android-x86/system-extras.git] / showmap / showmap.c
index 2a028ee..0a9a58a 100644 (file)
@@ -29,70 +29,108 @@ struct mapinfo {
 // 012345678901234567890123456789012345678901234567890123456789
 // 0         1         2         3         4         5
 
+int parse_header(char* line, int len, mapinfo** mi) {
+    unsigned long start;
+    unsigned long end;
+    char name[128];
+
+    name[0] = '\0';
+
+    // Sometimes the name is missing.
+    if (sscanf(line, "%lx-%lx %*s %*lx %*x:%*x %*ld %127s", &start, &end, name) < 2) {
+        return 0;
+    }
+
+    if (name[0] == '\0') {
+        if ((start >= 0x10000000) && (start < 0x40000000)) {
+            strlcpy(name, "[stack]", sizeof(name));
+        } else if (start > 0x50000000) {
+            strlcpy(name, "[lib_bss]", sizeof(name));
+        } else {
+            strlcpy(name, "[anon]", sizeof(name));
+        }
+    }
+
+    const int name_size = strlen(name) + 1;
+    struct mapinfo* info = calloc(1, sizeof(mapinfo) + name_size);
+    if (info == NULL) {
+        return -1;
+    }
+
+    info->start = start;
+    info->end = end;
+    strlcpy(info->name, name, name_size);
+
+    *mi = info;
+
+    return 0;
+}
+
+int parse_field(mapinfo* mi, char* line) {
+    char field[64];
+    int size;
+
+    if (sscanf(line, "%63s %d kB", field, &size) != 2) {
+        return -1;
+    }
+
+    if (!strcmp(field, "Size:")) {
+        mi->size = size;
+    } else if (!strcmp(field, "Rss:")) {
+        mi->rss = size;
+    } else if (!strcmp(field, "Pss:")) {
+        mi->pss = size;
+    } else if (!strcmp(field, "Shared_Clean:")) {
+        mi->shared_clean = size;
+    } else if (!strcmp(field, "Shared_Dirty:")) {
+        mi->shared_dirty = size;
+    } else if (!strcmp(field, "Private_Clean:")) {
+        mi->private_clean = size;
+    } else if (!strcmp(field, "Private_Dirty:")) {
+        mi->private_dirty = size;
+    }
+
+    return 0;
+}
+
 mapinfo *read_mapinfo(FILE *fp)
 {
     char line[1024];
-    mapinfo *mi;
+    mapinfo *current = NULL;
     int len;
     int skip;
 
-again:
-    skip = 0;
-    
-    if(fgets(line, 1024, fp) == 0) return 0;
+    while (fgets(line, sizeof(line), fp) != 0) {
+        if (current != NULL) {
+            parse_field(current, line);
+        }
+
+        len = strlen(line);
+        if (len < 1) {
+            return NULL;
+        }
+        line[--len] = 0;
 
-    len = strlen(line);
-    if(len < 1) return 0;
-    line[--len] = 0;
+        mapinfo *next = NULL;
+        if (parse_header(line, len, &next) < 0) {
+            goto err;
+        } else if (next != NULL) {
+            next->next = current;
+            current = next;
+            continue;
+        }
+    }
 
-    mi = calloc(1, sizeof(mapinfo) + len + 16);
-    if(mi == 0) return 0;
+    return current;
 
-    mi->start = strtoul(line, 0, 16);
-    mi->end = strtoul(line + 9, 0, 16);
+err:
+    while (current != NULL) {
+        mapinfo* next = current->next;
+        free(current);
+        current = next;
+    }
 
-    if(len < 50) {
-        if((mi->start >= 0x10000000) && (mi->start < 0x40000000)) {
-            strcpy(mi->name, "[stack]");
-        } else if(mi->start > 0x50000000) {
-            strcpy(mi->name, "[lib_bss]");
-        } else {
-            strcpy(mi->name, "[anon]");
-        }
-    } else {
-        strcpy(mi->name, line + 49);
-    }
-
-    if(fgets(line, 1024, fp) == 0) goto oops;
-    if(sscanf(line, "Size: %d kB", &mi->size) != 1) goto oops;
-    if(fgets(line, 1024, fp) == 0) goto oops;
-    if(sscanf(line, "Rss: %d kB", &mi->rss) != 1) goto oops;
-    if(fgets(line, 1024, fp) == 0) goto oops;
-    if(sscanf(line, "Pss: %d kB", &mi->pss) == 1)
-        if(fgets(line, 1024, fp) == 0) goto oops;
-    if(sscanf(line, "Shared_Clean: %d kB", &mi->shared_clean) != 1) goto oops;
-    if(fgets(line, 1024, fp) == 0) goto oops;
-    if(sscanf(line, "Shared_Dirty: %d kB", &mi->shared_dirty) != 1) goto oops;
-    if(fgets(line, 1024, fp) == 0) goto oops;
-    if(sscanf(line, "Private_Clean: %d kB", &mi->private_clean) != 1) goto oops;
-    if(fgets(line, 1024, fp) == 0) goto oops;
-    if(sscanf(line, "Private_Dirty: %d kB", &mi->private_dirty) != 1) goto oops;
-
-    if(fgets(line, 1024, fp) == 0) goto oops; // Referenced
-    if(fgets(line, 1024, fp) == 0) goto oops; // Swap
-    if(fgets(line, 1024, fp) == 0) goto oops; // KernelPageSize
-    if(fgets(line, 1024, fp) == 0) goto oops; // MMUPageSize
-
-    if(skip) {
-        free(mi);
-        goto again;
-    }
-
-    return mi;
-oops:
-    fprintf(stderr, "WARNING: Format of /proc/<pid>/smaps has changed!\n");
-    free(mi);
-    return 0;
+    return NULL;
 }
 
 
@@ -103,33 +141,51 @@ mapinfo *load_maps(int pid, int verbose)
     mapinfo *milist = 0;
     mapinfo *mi;
     
-    sprintf(tmp, "/proc/%d/smaps", pid);
+    snprintf(tmp, sizeof(tmp), "/proc/%d/smaps", pid);
     fp = fopen(tmp, "r");
-    if(fp == 0) return 0;
+    if (fp == 0) {
+        fprintf(stderr, "cannot open /proc/%d/smaps: %s\n", pid, strerror(errno));
+        return NULL;
+    }
+
+    milist = read_mapinfo(fp);
+    fclose(fp);
+
+    if (!milist) {
+        fprintf(stderr, "could not read /proc/%d/smaps\n", pid);
+        return NULL;
+    }
     
-    while((mi = read_mapinfo(fp)) != 0) {
-            /* if not verbose, coalesce mappings from the same entity */
-        if(!verbose && milist) {
-            if((!strcmp(mi->name, milist->name) && (mi->name[0] != '[')) 
-               || !strcmp(mi->name,"[lib_bss]")) {
-                milist->size += mi->size;
-                milist->rss += mi->rss;
-                milist->pss += mi->pss;
-                milist->shared_clean += mi->shared_clean;
-                milist->shared_dirty += mi->shared_dirty;
-                milist->private_clean += mi->private_clean;
-                milist->private_dirty += mi->private_dirty;
-                milist->end = mi->end;
-                free(mi);
-                continue;
+    /* if not verbose, coalesce mappings from the same entity */
+    if (!verbose) {
+        mapinfo* current = milist;
+        mapinfo* last = NULL;
+
+        while (current != NULL) {
+            mapinfo* next = current->next;
+
+            if (last != NULL
+                    && ((current->name[0] != '[' && !strcmp(last->name, current->name))
+                        || !strcmp(current->name, "[lib_bss]"))) {
+                last->size += current->size;
+                last->rss += current->rss;
+                last->pss += current->pss;
+                last->shared_clean += current->shared_clean;
+                last->shared_dirty += current->shared_dirty;
+                last->private_clean += current->private_clean;
+                last->private_dirty += current->private_dirty;
+                last->end = current->end;
+
+                last->next = next;
+                free(current);
+            } else {
+                last = current;
             }
-        }
 
-        mi->next = milist;
-        milist = mi;
+            current = next;
+        }
     }
-    fclose(fp);
-    
+
     return milist;
 }
 
@@ -150,12 +206,11 @@ int show_map(int pid)
     unsigned size = 0;
     
     milist = load_maps(pid, verbose);
-    if(milist == 0) {
-        fprintf(stderr,"cannot get /proc/smaps for pid %d\n", pid);
+    if (milist == NULL) {
         return 1;
     }
 
-    if(addresses) {
+    if (addresses) {
         printf("start    end      shared   private  object\n");
         printf("-------- -------- -------- -------- ------------------------------\n");
     } else {
@@ -163,7 +218,10 @@ int show_map(int pid)
         printf("size     RSS      PSS      clean    dirty    clean    dirty    object\n");
         printf("-------- -------- -------- -------- -------- -------- -------- ------------------------------\n");
     }
-    for(mi = milist; mi; mi = mi->next){
+
+    for (mi = milist; mi;) {
+        mapinfo* last = mi;
+
         shared_clean += mi->shared_clean;
         shared_dirty += mi->shared_dirty;
         private_clean += mi->private_clean;
@@ -172,9 +230,11 @@ int show_map(int pid)
         pss += mi->pss;
         size += mi->size;
         
-        if(terse && !mi->private_dirty) continue;
+        if (terse && !mi->private_dirty) {
+            goto out;
+        }
 
-        if(addresses) {
+        if (addresses) {
             printf("%08x %08x %8d %8d %s\n", mi->start, mi->end,
                    mi->shared_clean + mi->shared_dirty,
                    mi->private_clean + mi->private_dirty,
@@ -187,8 +247,13 @@ int show_map(int pid)
                    mi->private_clean, mi->private_dirty,
                    mi->name);
         }
+
+out:
+        mi = mi->next;
+        free(last);
     }
-    if(addresses) {
+
+    if (addresses) {
         printf("-------- -------- -------- -------- ------------------------------\n");
         printf("                  %8d %8d TOTAL\n", 
                shared_dirty + shared_clean, 
@@ -200,6 +265,7 @@ int show_map(int pid)
                shared_clean, shared_dirty,
                private_clean, private_dirty);
     }
+
     return 0;
 }
 
@@ -207,16 +273,16 @@ int main(int argc, char *argv[])
 {
     int usage = 1;
     
-    for(argc--, argv++; argc > 0; argc--, argv++) {
-        if(!strcmp(argv[0],"-v")) {
+    for (argc--, argv++; argc > 0; argc--, argv++) {
+        if (!strcmp(argv[0],"-v")) {
             verbose = 1;
             continue;
         }
-        if(!strcmp(argv[0],"-t")) {
+        if (!strcmp(argv[0],"-t")) {
             terse = 1;
             continue;
         }
-        if(!strcmp(argv[0],"-a")) {
+        if (!strcmp(argv[0],"-a")) {
             addresses = 1;
             continue;
         }
@@ -224,7 +290,7 @@ int main(int argc, char *argv[])
         usage = 0;
     }
 
-    if(usage) {
+    if (usage) {
         fprintf(stderr,
                 "showmap [-t] [-v] [-c] <pid>\n"
                 "        -t = terse (show only items with private pages)\n"
@@ -233,5 +299,5 @@ int main(int argc, char *argv[])
                 );
     }
 
-       return 0;
+    return 0;
 }