GpuService: Add 'help' and 'vkjson' commands

Bug: 26620936 and 27352427
Change-Id: Id0ad5b7184ddc624eddfb292a0d86546c26fb9ea
diff --git a/services/surfaceflinger/GpuService.cpp b/services/surfaceflinger/GpuService.cpp
index b20fc33..0c29971 100644
--- a/services/surfaceflinger/GpuService.cpp
+++ b/services/surfaceflinger/GpuService.cpp
@@ -17,6 +17,8 @@
 #include "GpuService.h"
 
 #include <binder/Parcel.h>
+#include <utils/String8.h>
+#include <vkjson.h>
 
 namespace android {
 
@@ -53,15 +55,121 @@
 
 // ----------------------------------------------------------------------------
 
+namespace {
+    status_t cmd_help(int out);
+    status_t cmd_vkjson(int out, int err);
+}
+
 const char* const GpuService::SERVICE_NAME = "gpu";
 
 GpuService::GpuService() {}
 
-status_t GpuService::shellCommand(int /*in*/, int /*out*/, int /*err*/,
-        Vector<String16>& /*args*/)
+status_t GpuService::shellCommand(int /*in*/, int out, int err,
+        Vector<String16>& args)
 {
-    ALOGD("GpuService::shellCommand");
+    ALOGV("GpuService::shellCommand");
+    for (size_t i = 0, n = args.size(); i < n; i++)
+        ALOGV("  arg[%zu]: '%s'", i, String8(args[i]).string());
+
+    if (args[0] == String16("vkjson"))
+        return cmd_vkjson(out, err);
+    else if (args[0] == String16("help"))
+        return cmd_help(out);
+
     return NO_ERROR;
 }
 
+// ----------------------------------------------------------------------------
+
+namespace {
+
+status_t cmd_help(int out) {
+    FILE* outs = fdopen(out, "w");
+    if (!outs) {
+        ALOGE("vkjson: failed to create out stream: %s (%d)", strerror(errno),
+            errno);
+        return BAD_VALUE;
+    }
+    fprintf(outs,
+        "GPU Service commands:\n"
+        "  vkjson   dump Vulkan device capabilities as JSON\n");
+    fclose(outs);
+    return NO_ERROR;
+}
+
+VkResult vkjsonPrint(FILE* out, FILE* err) {
+    VkResult result;
+
+    const VkApplicationInfo app_info = {
+        VK_STRUCTURE_TYPE_APPLICATION_INFO, nullptr,
+        "vkjson", 1,    /* app name, version */
+        "", 0,          /* engine name, version */
+        VK_API_VERSION
+    };
+    const VkInstanceCreateInfo instance_info = {
+        VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, nullptr,
+        0,              /* flags */
+        &app_info,
+        0, nullptr,     /* layers */
+        0, nullptr,     /* extensions */
+    };
+    VkInstance instance;
+    result = vkCreateInstance(&instance_info, nullptr, &instance);
+    if (result != VK_SUCCESS) {
+        fprintf(err, "vkCreateInstance failed: %d\n", result);
+        return result;
+    }
+
+    uint32_t ngpu = 0;
+    result = vkEnumeratePhysicalDevices(instance, &ngpu, nullptr);
+    if (result != VK_SUCCESS) {
+        fprintf(err, "vkEnumeratePhysicalDevices failed: %d\n", result);
+        return result;
+    }
+    std::vector<VkPhysicalDevice> gpus(ngpu, VK_NULL_HANDLE);
+    result = vkEnumeratePhysicalDevices(instance, &ngpu, gpus.data());
+    if (result != VK_SUCCESS) {
+        fprintf(err, "vkEnumeratePhysicalDevices failed: %d\n", result);
+        return result;
+    }
+
+    for (size_t i = 0, n = gpus.size(); i < n; i++) {
+        auto props = VkJsonGetAllProperties(gpus[i]);
+        std::string json = VkJsonAllPropertiesToJson(props);
+        fwrite(json.data(), 1, json.size(), out);
+        if (i < n - 1)
+            fputc(',', out);
+        fputc('\n', out);
+    }
+
+    vkDestroyInstance(instance, nullptr);
+
+    return VK_SUCCESS;
+}
+
+status_t cmd_vkjson(int out, int err) {
+    int errnum;
+    FILE* outs = fdopen(out, "w");
+    if (!outs) {
+        errnum = errno;
+        ALOGE("vkjson: failed to create output stream: %s", strerror(errnum));
+        return -errnum;
+    }
+    FILE* errs = fdopen(err, "w");
+    if (!errs) {
+        errnum = errno;
+        ALOGE("vkjson: failed to create error stream: %s", strerror(errnum));
+        fclose(outs);
+        return -errnum;
+    }
+    fprintf(outs, "[\n");
+    VkResult result = vkjsonPrint(outs, errs);
+    fprintf(outs, "]\n");
+    fclose(errs);
+    fclose(outs);
+    return result >= 0 ? NO_ERROR : UNKNOWN_ERROR;
+}
+
+} // anonymous namespace
+
 } // namespace android