Add json and csv format ouput option to librank tool

Add an option to librank. It will print the output in json/csv/raw
format based on the argument provided by user.

Bug: 152634898
Test: Built librank and tested on wembley device. The output is as
expected

Change-Id: I6731072c68469a4aba169888d357267219da9256
diff --git a/tools/librank.cpp b/tools/librank.cpp
index e53c746..d6a363e 100644
--- a/tools/librank.cpp
+++ b/tools/librank.cpp
@@ -41,6 +41,13 @@
 using ::android::meminfo::ProcMemInfo;
 using ::android::meminfo::Vma;
 
+// The output format that can be specifid by user.
+enum Format {
+    RAW = 0,
+    JSON,
+    CSV
+};
+
 [[noreturn]] static void usage(int exit_status) {
     fprintf(stderr,
             "Usage: %s [ -P | -L ] [ -v | -r | -p | -u | -s | -h ]\n"
@@ -59,6 +66,8 @@
             "    -c  Only show cached (storage backed) pages\n"
             "    -C  Only show non-cached (ram/swap backed) pages\n"
             "    -k  Only show pages collapsed by KSM\n"
+            "    -f  [raw][json][csv] Print output in the specified format.\n"
+            "        (Default format is raw text.)\n"
             "    -h  Display this help screen.\n",
             getprogname());
     exit(exit_status);
@@ -230,6 +239,98 @@
     return ret;
 }
 
+static std::string escape_csv_string(const std::string& raw) {
+  std::string ret;
+  for (auto it = raw.cbegin(); it != raw.cend(); it++) {
+    switch (*it) {
+      case '"':
+        ret += "\"\"";
+        break;
+      default:
+        ret += *it;
+        break;
+    }
+  }
+  return '"' + ret + '"';
+}
+
+std::string to_csv(LibRecord& l, ProcessRecord& p) {
+    const MemUsage& usage = p.usage();
+    return  escape_csv_string(l.name())
+            + "," + std::to_string(l.pss() / 1024)
+            + "," + escape_csv_string(p.cmdline())
+            + ",\"[" + std::to_string(p.pid()) + "]\""
+            + "," + std::to_string(usage.vss/1024)
+            + "," + std::to_string(usage.rss/1024)
+            + "," + std::to_string(usage.pss/1024)
+            + "," + std::to_string(usage.uss/1024)
+            + (g_has_swap ? "," + std::to_string(usage.swap/1024) : "");
+}
+
+static std::string escape_json_string(const std::string& raw) {
+  std::string ret;
+  for (auto it = raw.cbegin(); it != raw.cend(); it++) {
+    switch (*it) {
+      case '\\':
+        ret += "\\\\";
+        break;
+      case '"':
+        ret += "\\\"";
+        break;
+      case '/':
+        ret += "\\/";
+        break;
+      case '\b':
+        ret += "\\b";
+        break;
+      case '\f':
+        ret += "\\f";
+        break;
+      case '\n':
+        ret += "\\n";
+        break;
+      case '\r':
+        ret += "\\r";
+        break;
+      case '\t':
+        ret += "\\t";
+        break;
+      default:
+        ret += *it;
+        break;
+    }
+  }
+  return '"' + ret + '"';
+}
+
+std::string to_json(LibRecord& l, ProcessRecord& p) {
+    const MemUsage& usage = p.usage();
+    return "{\"Library\":" + escape_json_string(l.name())
+            + ",\"Total_RSS\":" + std::to_string(l.pss() / 1024)
+            + ",\"Process\":" + escape_json_string(p.cmdline())
+            + ",\"PID\":\"" + std::to_string(p.pid()) + "\""
+            + ",\"VSS\":" + std::to_string(usage.vss/1024)
+            + ",\"RSS\":" + std::to_string(usage.rss/1024)
+            + ",\"PSS\":" + std::to_string(usage.pss/1024)
+            + ",\"USS\":" + std::to_string(usage.uss/1024)
+            + (g_has_swap ? ",\"Swap\":" + std::to_string(usage.swap/1024) : "")
+            + "}";
+}
+
+static Format get_format(std::string arg) {
+    if (arg.compare("json") == 0) {
+        return JSON;
+    }
+    if (arg.compare("csv") == 0) {
+        return CSV;
+    }
+    if (arg.compare("raw") == 0) {
+        return RAW;
+    }
+    error(EXIT_FAILURE, 0, "Invalid format.");
+    return RAW;
+}
+
 int main(int argc, char* argv[]) {
     int opt;
 
@@ -255,7 +356,8 @@
 
     std::function<bool(const ProcessRecord&, const ProcessRecord&)> sort_func = pss_sort;
 
-    while ((opt = getopt(argc, argv, "acChkm:pP:uvrsR")) != -1) {
+    Format format = RAW;
+    while ((opt = getopt(argc, argv, "acCf:hkm:pP:uvrsR")) != -1) {
         switch (opt) {
             case 'a':
                 g_all_libs = true;
@@ -267,6 +369,9 @@
             case 'C':
                 g_pgflags = g_pgflags_mask = (1 << KPF_SWAPBACKED);
                 break;
+            case 'f':
+                format = get_format(optarg);
+                break;
             case 'h':
                 usage(EXIT_SUCCESS);
             case 'k':
@@ -305,11 +410,24 @@
         error(EXIT_FAILURE, 0, "Failed to read all pids from the system");
     }
 
-    printf(" %6s   %7s   %6s   %6s   %6s  ", "RSStot", "VSS", "RSS", "PSS", "USS");
-    if (g_has_swap) {
-        printf(" %6s  ", "Swap");
+    switch (format) {
+        case RAW:
+            printf(" %6s   %7s   %6s   %6s   %6s  ", "RSStot", "VSS", "RSS", "PSS", "USS");
+            if (g_has_swap) {
+                printf(" %6s  ", "Swap");
+            }
+            printf("Name/PID\n");
+            break;
+        case CSV:
+            printf("\"Library\",\"Total_RSS\",\"Process\",\"PID\",\"VSS\",\"RSS\",\"PSS\",\"USS\"");
+            if (g_has_swap) {
+                printf(", \"Swap\"");
+            }
+            printf("\n");
+            break;
+        case JSON:
+            break;
     }
-    printf("Name/PID\n");
 
     std::vector<LibRecord> v_libs;
     v_libs.reserve(g_libs.size());
@@ -321,11 +439,13 @@
               [](const LibRecord& l1, const LibRecord& l2) { return l1.pss() > l2.pss(); });
 
     for (auto& lib : v_libs) {
-        printf("%6" PRIu64 "K   %7s   %6s   %6s   %6s  ", lib.pss() / 1024, "", "", "", "");
-        if (g_has_swap) {
-            printf(" %6s  ", "");
+        if (format == RAW) {
+            printf("%6" PRIu64 "K   %7s   %6s   %6s   %6s  ", lib.pss() / 1024, "", "", "", "");
+            if (g_has_swap) {
+                printf(" %6s  ", "");
+            }
+            printf("%s\n", lib.name().c_str());
         }
-        printf("%s\n", lib.name().c_str());
 
         // sort all mappings first
 
@@ -338,12 +458,22 @@
 
         for (auto& p : procs) {
             const MemUsage& usage = p.usage();
-            printf(" %6s  %7" PRIu64 "K  %6" PRIu64 "K  %6" PRIu64 "K  %6" PRIu64 "K  ", "",
-                   usage.vss / 1024, usage.rss / 1024, usage.pss / 1024, usage.uss / 1024);
-            if (g_has_swap) {
-                printf("%6" PRIu64 "K  ", usage.swap / 1024);
+            switch (format) {
+                case RAW:
+                    printf(" %6s  %7" PRIu64 "K  %6" PRIu64 "K  %6" PRIu64 "K  %6" PRIu64 "K  ", "",
+                        usage.vss / 1024, usage.rss / 1024, usage.pss / 1024, usage.uss / 1024);
+                    if (g_has_swap) {
+                        printf("%6" PRIu64 "K  ", usage.swap / 1024);
+                    }
+                    printf("  %s [%d]\n", p.cmdline().c_str(), p.pid());
+                    break;
+                case JSON:
+                    printf("%s\n", to_json(lib, p).c_str());
+                    break;
+                case CSV:
+                    printf("%s\n", to_csv(lib, p).c_str());
+                    break;
             }
-            printf("  %s [%d]\n", p.cmdline().c_str(), p.pid());
         }
     }