diff --git a/README.md b/README.md index 8798f44..1cc6d51 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,9 @@ ferret.py. Build ===== +``` +git submodule update --init --recursive +``` Linux ----- @@ -69,6 +72,9 @@ Example: Using as a layer (Vulkan only) ============================== +Linux +----- + Once built, the layer and json manifest will be in /implicit_layer.d Set the following env. vars to enable the layer on linux: @@ -86,8 +92,24 @@ Then set the following env var to point to your libcollector JSON configuration export VK_LIBCOLLECTOR_CONFIG_PATH= ``` +Android +------- + +Local file to push to the location on Android specified: + +```bash +adb push arm64/libVkLayer_libcollector.so /data/app/{app_path}/lib/arm64/ +adb push layer/libcollector_config.json /sdcard/Download/ +``` + +Set the following env. vars to enable the layer on Android: + +```bash +adb shell setprop debug.vulkan.layer.1 VK_LAYER_ARM_libcollector +``` + Then run your app as normal. One result file will be created per device in your application, and by default the results are written to the run directory. -To override this, set the "result_file_basename" field in the config json. +To override this, set the "result_file_basename" field in the config json. Get the result file in the specified location, for example: /sdcard/Download/results_device_0.json JSON interface (layer specific) --------------------------- diff --git a/android/gradle/collector_android/CMakeLists.txt b/android/gradle/collector_android/CMakeLists.txt index 63ad950..fff33a8 100644 --- a/android/gradle/collector_android/CMakeLists.txt +++ b/android/gradle/collector_android/CMakeLists.txt @@ -49,3 +49,4 @@ find_library(android-lib android) add_executable(burrow ${BURROW_SOURCES}) target_link_libraries(burrow app-glue ${log-lib} ${android-lib} ${app-glue} collector_android) +target_link_options(burrow PRIVATE -Wl,-z,max-page-size=16384) diff --git a/android/gradle/layer_android/CMakeLists.txt b/android/gradle/layer_android/CMakeLists.txt index 9bfe633..0ba2ad0 100644 --- a/android/gradle/layer_android/CMakeLists.txt +++ b/android/gradle/layer_android/CMakeLists.txt @@ -45,3 +45,4 @@ find_library(android-lib android) add_library(VkLayer_libcollector SHARED ${LAYER_SOURCES}) target_link_libraries(VkLayer_libcollector ${log-lib} ${android-lib}) +target_link_options(VkLayer_libcollector PRIVATE -Wl,-z,max-page-size=16384) diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 3524af3..b3bbda1 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -69,6 +69,7 @@ LOCAL_C_INCLUDES := \ LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog -latomic +LOCAL_LDFLAGS += -Wl,-z,max-page-size=16384 LOCAL_STATIC_LIBRARIES := collector_android LOCAL_CPP_FEATURES += exceptions diff --git a/layer/libcollector_config.json b/layer/libcollector_config.json new file mode 100644 index 0000000..9369ed0 --- /dev/null +++ b/layer/libcollector_config.json @@ -0,0 +1,38 @@ +{ + "result_file_basename" : "/sdcard/Download/results.json", + "num_subframes_per_frame" : 1, + "frame_trigger_function" : "vkQueuePresentKHR", + "start_frame" : 0, + "end_frame" : 99999, + "collectors": { + "perf": { + "set": 4, + "event": [ + { + "name": "CPUCyclesUser", + "type": 4, + "config": 17, + "excludeKernel": true + }, + { + "name": "CPUCyclesKernel", + "type": 4, + "config": 17, + "excludeUser": true + }, + { + "name": "CPUInstructionUser", + "type": 4, + "config": 8, + "excludeKernel": true + }, + { + "name": "CPUInstructionKernel", + "type": 4, + "config": 8, + "excludeUser": true + } + ] + } + } +} diff --git a/layer/vulkan_layer.cpp b/layer/vulkan_layer.cpp index 2442393..c7aff6a 100644 --- a/layer/vulkan_layer.cpp +++ b/layer/vulkan_layer.cpp @@ -1,7 +1,7 @@ #include "layer/vulkan_layer.hpp" static std::mutex context_mutex; -static std::unordered_map instance_dispatch_map; +static std::unordered_map instance_dispatch_map; static std::unordered_map device_to_context_map; static std::unordered_map queue_to_context_map; @@ -11,12 +11,21 @@ static Json::Value glibcollector_config; std::mutex vkCollectorContext::id_mutex; uint32_t vkCollectorContext::next_id = 0; +template +void *get_key(dispatchable_type inst) { + return *reinterpret_cast(inst); +} + +#define GET_PROC_ADDR(func) \ + if (!strcmp(funcName, #func)) \ + return (PFN_vkVoidFunction)&lc_##func; + std::string get_config_path() { std::string config_path = "libcollector_config.json"; #ifdef ANDROID // TODO(tomped01): Find a better way on android - config_path = "/sdcard/libcollector_config.json"; + config_path = "/sdcard/Download/libcollector_config.json"; #else const char* env_path = std::getenv("VK_LIBCOLLECTOR_CONFIG_PATH"); if (env_path) { @@ -68,7 +77,7 @@ bool read_config() { if (glibcollector_config.get("result_file_basename", "").asString() == "") { #ifdef ANDROID - glibcollector_config["result_file_basename"] = "/sdcard/results.json"; + glibcollector_config["result_file_basename"] = "/sdcard/Download/results.json"; #else glibcollector_config["result_file_basename"] = "results.json"; #endif @@ -86,54 +95,6 @@ bool read_config() { return true; } - -VK_LAYER_EXPORT PFN_vkVoidFunction VKAPI_CALL vkGetInstanceProcAddr(VkInstance instance, const char *pName) { - if (!gconfig_initialized) { - // Do this as early as possible - read_config(); - - gconfig_initialized = true; - } - - if(!strcmp(pName, "vkGetInstanceProcAddr")) return (PFN_vkVoidFunction)&vkGetInstanceProcAddr; - if(!strcmp(pName, "vkCreateInstance")) return (PFN_vkVoidFunction)&lc_vkCreateInstance; - if(!strcmp(pName, "vkCreateDevice")) return (PFN_vkVoidFunction)&lc_vkCreateDevice; - if(!strcmp(pName, "vkDestroyInstance")) return (PFN_vkVoidFunction)&lc_vkDestroyInstance; - if(!strcmp(pName, "vkEnumerateInstanceLayerProperties")) return (PFN_vkVoidFunction)&lc_vkEnumerateInstanceLayerProperties; - if(!strcmp(pName, "vkEnumerateInstanceExtensionProperties")) return (PFN_vkVoidFunction)&lc_vkEnumerateInstanceExtensionProperties; - - context_mutex.lock(); - InstanceDispatchTable* instance_dtable = instance_dispatch_map[instance]; - PFN_vkGetInstanceProcAddr gipa = instance_dtable->gipa; - context_mutex.unlock(); - - return gipa(instance, pName); -} - - -VK_LAYER_EXPORT PFN_vkVoidFunction VKAPI_CALL vkGetDeviceProcAddr(VkDevice device, const char* pName) { - if(!strcmp(pName, "vkGetDeviceProcAddr")) return (PFN_vkVoidFunction)&vkGetDeviceProcAddr; - if(!strcmp(pName, "vkDestroyDevice")) return (PFN_vkVoidFunction)&lc_vkDestroyDevice; - if(!strcmp(pName, "vkGetDeviceQueue")) return (PFN_vkVoidFunction)&lc_vkGetDeviceQueue; - - if (glibcollector_config.get("frame_trigger_function", "").asString() == std::string("vkQueueSubmit")) { - DBG_LOG("LIBCOLLECTOR LAYER: Sampling function set to vkQueueSubmit\n"); - if(!strcmp(pName, "vkQueueSubmit")) return (PFN_vkVoidFunction)&lc_vkQueueSubmit; - } else { - DBG_LOG("LIBCOLLECTOR LAYER: Sampling function set to vkQueuePresentKHR\n"); - if(!strcmp(pName, "vkQueuePresentKHR")) return (PFN_vkVoidFunction)&lc_vkQueuePresentKHR; - } - - context_mutex.lock(); - vkCollectorContext* context = device_to_context_map[device]; - PFN_vkGetDeviceProcAddr gdpa; - gdpa = context->gdpa; - context_mutex.unlock(); - - return gdpa(device, pName); -} - - VK_LAYER_EXPORT VkResult VKAPI_CALL lc_vkCreateInstance( const VkInstanceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, @@ -158,9 +119,10 @@ VK_LAYER_EXPORT VkResult VKAPI_CALL lc_vkCreateInstance( InstanceDispatchTable* new_table = new InstanceDispatchTable(); new_table->gipa = (PFN_vkGetInstanceProcAddr)gipa(*pInstance, "vkGetInstanceProcAddr"); new_table->nextDestroyInstance = (PFN_vkDestroyInstance)gipa(*pInstance, "vkDestroyInstance"); + new_table->nextEnumerateDeviceExtensionProperties = (PFN_vkEnumerateDeviceExtensionProperties)gipa(*pInstance, "vkEnumerateDeviceExtensionProperties"); context_mutex.lock(); - instance_dispatch_map[*pInstance] = new_table; + instance_dispatch_map[get_key(*pInstance)] = new_table; context_mutex.unlock(); @@ -173,9 +135,9 @@ VK_LAYER_EXPORT void VKAPI_CALL lc_vkDestroyInstance( const VkAllocationCallbacks* pAllocator ) { context_mutex.lock(); - InstanceDispatchTable* table = instance_dispatch_map[instance]; + InstanceDispatchTable* table = instance_dispatch_map[get_key(instance)]; PFN_vkDestroyInstance nextDestroyInstance = table->nextDestroyInstance; - instance_dispatch_map.erase(instance); + instance_dispatch_map.erase(get_key(instance)); context_mutex.unlock(); delete table; @@ -302,9 +264,9 @@ VK_LAYER_EXPORT VkResult VKAPI_CALL lc_vkQueuePresentKHR( } -// Pre-instance functions +// Enum-instance functions -VK_LAYER_EXPORT VkResult VKAPI_CALL lc_vkEnumerateInstanceLayerProperties( +VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateInstanceLayerProperties( uint32_t* pPropertyCount, VkLayerProperties* pProperties ) { @@ -322,7 +284,17 @@ VK_LAYER_EXPORT VkResult VKAPI_CALL lc_vkEnumerateInstanceLayerProperties( return VK_SUCCESS; } -VK_LAYER_EXPORT VkResult VKAPI_CALL lc_vkEnumerateInstanceExtensionProperties( + +VK_LAYER_EXPORT VkResult VKAPI_CALL vkEnumerateDeviceLayerProperties( + VkPhysicalDevice physicalDevice, + uint32_t *pPropertyCount, + VkLayerProperties *pProperties +) { + return vkEnumerateInstanceLayerProperties(pPropertyCount, pProperties); +} + + +VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateInstanceExtensionProperties( const char* pLayerName, uint32_t* pPropertyCount, VkExtensionProperties* pProperties @@ -337,39 +309,91 @@ VK_LAYER_EXPORT VkResult VKAPI_CALL lc_vkEnumerateInstanceExtensionProperties( return VK_ERROR_LAYER_NOT_PRESENT; } -// Pre-instance interface functions, not currently in use, but can be enabled if/when we update to manifest format 1.2.1 +VK_LAYER_EXPORT VkResult VKAPI_CALL vkEnumerateDeviceExtensionProperties(VkPhysicalDevice physicalDevice, + const char *pLayerName, + uint32_t *pPropertyCount, + VkExtensionProperties *pProperties) +{ + if (pLayerName == NULL || strcmp(pLayerName, "VK_LAYER_ARM_libcollector")) { + if (physicalDevice == VK_NULL_HANDLE) + { + return VK_SUCCESS; + } -VkResult lc_pre_vkEnumerateInstanceExtensionProperties( - const VkEnumerateInstanceExtensionPropertiesChain* pChain, - const char* pLayerName, - uint32_t* pPropertyCount, - VkExtensionProperties* pProperties -) { - if (pLayerName == NULL) { - return VK_ERROR_LAYER_NOT_PRESENT; - } else if (!strcmp(pLayerName, "VK_LAYER_ARM_libcollector")) { - return pChain->pfnNextLayer(pChain->pNextLink, pLayerName, pPropertyCount, pProperties); + return instance_dispatch_map[get_key(physicalDevice)]->nextEnumerateDeviceExtensionProperties( + physicalDevice, pLayerName, pPropertyCount, pProperties); } - return VK_ERROR_LAYER_NOT_PRESENT; + if (pPropertyCount != nullptr) { + *pPropertyCount = 0; + } + + return VK_SUCCESS; } -VkResult lc_pre_vkEnumerateInstanceLayerProperties( - const VkEnumerateInstanceLayerPropertiesChain* pChain, - uint32_t* pPropertyCount, - VkLayerProperties* pProperties -) { - if (pProperties == nullptr) { - *pPropertyCount = 1; + +VK_LAYER_EXPORT PFN_vkVoidFunction VKAPI_CALL lc_vkGetDeviceProcAddr(VkDevice device, const char *funcName) { + GET_PROC_ADDR(vkGetDeviceProcAddr); + GET_PROC_ADDR(vkDestroyDevice); + GET_PROC_ADDR(vkGetDeviceQueue); + + if (glibcollector_config.get("frame_trigger_function", "").asString() == std::string("vkQueueSubmit")) { + DBG_LOG("LIBCOLLECTOR LAYER: Sampling function set to vkQueueSubmit\n"); + GET_PROC_ADDR(vkQueueSubmit); } else { - const char* layer_name = "VK_LAYER_ARM_libcollector"; - strncpy(pProperties[0].layerName, layer_name, strlen(layer_name) + 1); - pProperties[0].specVersion = VK_MAKE_API_VERSION(0, 1, 1, 2); - pProperties[0].implementationVersion = 2; - const char* layer_description = "ARM libcollector layer implementation."; - strncpy(pProperties[0].description, layer_description, strlen(layer_description) + 1); + DBG_LOG("LIBCOLLECTOR LAYER: Sampling function set to vkQueuePresentKHR\n"); + GET_PROC_ADDR(vkQueuePresentKHR); } + context_mutex.lock(); + vkCollectorContext* context = device_to_context_map[device]; + PFN_vkGetDeviceProcAddr gdpa; + gdpa = context->gdpa; + context_mutex.unlock(); + + return gdpa(device, funcName); +} + + +VK_LAYER_EXPORT PFN_vkVoidFunction VKAPI_CALL vkGetDeviceProcAddr(VkDevice device, const char *funcName) { + return lc_vkGetDeviceProcAddr(device, funcName); +} + + +VK_LAYER_EXPORT PFN_vkVoidFunction VKAPI_CALL lc_vkGetInstanceProcAddr(VkInstance instance, + const char *funcName) { + if (!gconfig_initialized) { + // Do this as early as possible + read_config(); + + gconfig_initialized = true; + } + + GET_PROC_ADDR(vkGetInstanceProcAddr); + GET_PROC_ADDR(vkCreateInstance); + GET_PROC_ADDR(vkCreateDevice); + GET_PROC_ADDR(vkDestroyInstance); + + context_mutex.lock(); + InstanceDispatchTable* instance_dtable = instance_dispatch_map[get_key(instance)]; + PFN_vkGetInstanceProcAddr gipa = instance_dtable->gipa; + context_mutex.unlock(); + + return gipa(instance, funcName); +} + + +VK_LAYER_EXPORT PFN_vkVoidFunction VKAPI_CALL vkGetInstanceProcAddr(VkInstance instance, const char *funcName) { + return lc_vkGetInstanceProcAddr(instance, funcName); +} + + +VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL +vkNegotiateLoaderLayerInterfaceVersion(VkNegotiateLayerInterface *pVersionStruct) { + pVersionStruct->pfnGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)&lc_vkGetInstanceProcAddr; + pVersionStruct->pfnGetDeviceProcAddr = (PFN_vkGetDeviceProcAddr)&lc_vkGetDeviceProcAddr; + pVersionStruct->pfnGetPhysicalDeviceProcAddr = nullptr; + return VK_SUCCESS; } diff --git a/layer/vulkan_layer.hpp b/layer/vulkan_layer.hpp index 0f74127..3111096 100644 --- a/layer/vulkan_layer.hpp +++ b/layer/vulkan_layer.hpp @@ -29,15 +29,9 @@ struct vkCollectorContext; struct InstanceDispatchTable { PFN_vkGetInstanceProcAddr gipa; PFN_vkDestroyInstance nextDestroyInstance; + PFN_vkEnumerateDeviceExtensionProperties nextEnumerateDeviceExtensionProperties; }; -#ifdef ANDROID -struct InstanceEnumerationTable { - PFN_vkEnumerateInstanceLayerProperties nextEnumerateInstanceLayerProperties; - PFN_vkEnumerateInstanceExtensionProperties nextEnumerateInstanceExtensionProperties; -}; -#endif - struct vkCollectorContext { vkCollectorContext(); ~vkCollectorContext(); @@ -84,64 +78,4 @@ struct vkCollectorContext { std::string get_config_path(); bool read_config(); -VK_LAYER_EXPORT PFN_vkVoidFunction VKAPI_CALL vkGetInstanceProcAddr(VkInstance instance, const char *pName); - -VK_LAYER_EXPORT PFN_vkVoidFunction VKAPI_CALL vkGetDeviceProcAddr(VkDevice device, const char* pName); - -VK_LAYER_EXPORT VkResult VKAPI_CALL lc_vkEnumerateInstanceLayerProperties( - uint32_t* pPropertyCount, - VkLayerProperties* pProperties); - -VK_LAYER_EXPORT VkResult VKAPI_CALL lc_vkEnumerateInstanceExtensionProperties( - const char* pLayerName, - uint32_t* pPropertyCount, - VkExtensionProperties* pProperties); - -VK_LAYER_EXPORT VkResult VKAPI_CALL lc_vkCreateInstance( - const VkInstanceCreateInfo* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkInstance* pInstance); - -VK_LAYER_EXPORT void VKAPI_CALL lc_vkDestroyInstance( - VkInstance instance, - const VkAllocationCallbacks* pAllocator); - -VK_LAYER_EXPORT VkResult VKAPI_CALL lc_vkCreateDevice( - VkPhysicalDevice physicalDevice, - const VkDeviceCreateInfo* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkDevice* pDevice); - -VK_LAYER_EXPORT void VKAPI_CALL lc_vkDestroyDevice( - VkDevice device, - const VkAllocationCallbacks* pAllocator); - -VK_LAYER_EXPORT void VKAPI_CALL lc_vkGetDeviceQueue( - VkDevice device, - uint32_t queueFamilyIndex, - uint32_t queueIndex, - VkQueue* pQueue); - -VK_LAYER_EXPORT VkResult VKAPI_CALL lc_vkQueueSubmit( - VkQueue queue, - uint32_t submitCount, - const VkSubmitInfo* pSubmits, - VkFence fence); - -VK_LAYER_EXPORT VkResult VKAPI_CALL lc_vkQueuePresentKHR( - VkQueue queue, - const VkPresentInfoKHR* pPresentInfo); - -VkResult lc_pre_vkEnumerateInstanceExtensionProperties( - const VkEnumerateInstanceExtensionPropertiesChain* pChain, - const char* pLayerName, - uint32_t* pPropertyCount, - VkExtensionProperties* pProperties); - -VkResult lc_pre_vkEnumerateInstanceLayerProperties( - const VkEnumerateInstanceLayerPropertiesChain* pChain, - uint32_t* pPropertyCount, - VkLayerProperties* pProperties); - - #endif