blob: 7da370ce22b678e9e8096e8ad6236934ee3dd673 [file] [log] [blame]
Courtney Goeltzenleuchterf579fa62015-06-10 17:39:03 -06001/*
Jon Ashburn23d36b12016-02-02 17:47:28 -07002 * Copyright (c) 2015-2016 The Khronos Group Inc.
3 * Copyright (c) 2015-2016 Valve Corporation
4 * Copyright (c) 2015-2016 LunarG, Inc.
5 * Copyright (C) 2015-2016 Google Inc.
Courtney Goeltzenleuchterf579fa62015-06-10 17:39:03 -06006 *
Jon Ashburn23d36b12016-02-02 17:47:28 -07007 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and/or associated documentation files (the "Materials"), to
9 * deal in the Materials without restriction, including without limitation the
10 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
11 * sell copies of the Materials, and to permit persons to whom the Materials are
12 * furnished to do so, subject to the following conditions:
Courtney Goeltzenleuchterf579fa62015-06-10 17:39:03 -060013 *
Jon Ashburn23d36b12016-02-02 17:47:28 -070014 * The above copyright notice(s) and this permission notice shall be included in
15 * all copies or substantial portions of the Materials.
Courtney Goeltzenleuchterf579fa62015-06-10 17:39:03 -060016 *
Jon Ashburn23d36b12016-02-02 17:47:28 -070017 * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
Courtney Goeltzenleuchterf579fa62015-06-10 17:39:03 -060018 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
Jon Ashburn23d36b12016-02-02 17:47:28 -070019 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20 *
21 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
22 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
23 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE
24 * USE OR OTHER DEALINGS IN THE MATERIALS.
Courtney Goeltzenleuchterf579fa62015-06-10 17:39:03 -060025 *
Courtney Goeltzenleuchter05559522015-10-30 11:14:30 -060026 * Author: Courtney Goeltzenleuchter <courtney@LunarG.com>
Jon Ashburn23d36b12016-02-02 17:47:28 -070027 * Author: Jon Ashburn <jon@LunarG.com>
Courtney Goeltzenleuchter05559522015-10-30 11:14:30 -060028 *
Courtney Goeltzenleuchterf579fa62015-06-10 17:39:03 -060029 */
30
Courtney Goeltzenleuchter7f5aafc2015-07-05 11:28:29 -060031#define _GNU_SOURCE
Courtney Goeltzenleuchter7dcc6a72015-06-11 16:01:11 -060032#include <stdio.h>
Courtney Goeltzenleuchterf579fa62015-06-10 17:39:03 -060033#include <string.h>
34#include <stdlib.h>
Courtney Goeltzenleuchter7dcc6a72015-06-11 16:01:11 -060035#include <inttypes.h>
Tony Barbour1d825c72015-06-18 16:29:32 -060036#ifndef WIN32
Courtney Goeltzenleuchter03663d02015-07-22 11:01:53 -060037#include <signal.h>
38#else
Tony Barbour1d825c72015-06-18 16:29:32 -060039#endif
Jon Ashburn480a50a2015-08-28 14:58:46 -070040#include "vk_loader_platform.h"
Courtney Goeltzenleuchterf579fa62015-06-10 17:39:03 -060041#include "debug_report.h"
David Pinedo9316d3b2015-11-06 12:54:48 -070042#include "vulkan/vk_layer.h"
Courtney Goeltzenleuchterf579fa62015-06-10 17:39:03 -060043
Jon Ashburn23d36b12016-02-02 17:47:28 -070044typedef void(VKAPI_PTR *PFN_stringCallback)(char *message);
Courtney Goeltzenleuchter7dcc6a72015-06-11 16:01:11 -060045
Jon Ashburn5c042ea2015-08-04 11:14:18 -060046static const VkExtensionProperties debug_report_extension_info = {
Jon Ashburn23d36b12016-02-02 17:47:28 -070047 .extensionName = VK_EXT_DEBUG_REPORT_EXTENSION_NAME,
48 .specVersion = VK_EXT_DEBUG_REPORT_SPEC_VERSION,
Courtney Goeltzenleuchterf579fa62015-06-10 17:39:03 -060049};
50
51void debug_report_add_instance_extensions(
Jon Ashburn23d36b12016-02-02 17:47:28 -070052 const struct loader_instance *inst,
53 struct loader_extension_list *ext_list) {
Jon Ashburne39a4f82015-08-28 13:38:21 -060054 loader_add_to_ext_list(inst, ext_list, 1, &debug_report_extension_info);
Courtney Goeltzenleuchterf579fa62015-06-10 17:39:03 -060055}
56
Jon Ashburn23d36b12016-02-02 17:47:28 -070057void debug_report_create_instance(struct loader_instance *ptr_instance,
58 const VkInstanceCreateInfo *pCreateInfo) {
Courtney Goeltzenleuchter110fdf92015-06-29 15:39:26 -060059 ptr_instance->debug_report_enabled = false;
60
Jon Ashburnf19916e2016-01-11 13:12:43 -070061 for (uint32_t i = 0; i < pCreateInfo->enabledExtensionCount; i++) {
Jon Ashburn23d36b12016-02-02 17:47:28 -070062 if (strcmp(pCreateInfo->ppEnabledExtensionNames[i],
63 VK_EXT_DEBUG_REPORT_EXTENSION_NAME) == 0) {
Courtney Goeltzenleuchter110fdf92015-06-29 15:39:26 -060064 ptr_instance->debug_report_enabled = true;
65 return;
66 }
67 }
Courtney Goeltzenleuchterf579fa62015-06-10 17:39:03 -060068}
69
Jon Ashburn23d36b12016-02-02 17:47:28 -070070VkResult
71util_CreateDebugReportCallback(struct loader_instance *inst,
72 VkDebugReportCallbackCreateInfoEXT *pCreateInfo,
73 const VkAllocationCallbacks *pAllocator,
74 VkDebugReportCallbackEXT callback) {
Courtney Goeltzenleuchter82887272015-12-02 15:29:33 -070075 VkLayerDbgFunctionNode *pNewDbgFuncNode;
76 if (pAllocator != NULL) {
Jon Ashburn23d36b12016-02-02 17:47:28 -070077 pNewDbgFuncNode = (VkLayerDbgFunctionNode *)pAllocator->pfnAllocation(
78 pAllocator->pUserData, sizeof(VkLayerDbgFunctionNode),
79 sizeof(int *), VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
Courtney Goeltzenleuchter82887272015-12-02 15:29:33 -070080 } else {
Jon Ashburn23d36b12016-02-02 17:47:28 -070081 pNewDbgFuncNode = (VkLayerDbgFunctionNode *)loader_heap_alloc(
82 inst, sizeof(VkLayerDbgFunctionNode),
83 VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
Courtney Goeltzenleuchter82887272015-12-02 15:29:33 -070084 }
85 if (!pNewDbgFuncNode)
86 return VK_ERROR_OUT_OF_HOST_MEMORY;
87
88 pNewDbgFuncNode->msgCallback = callback;
89 pNewDbgFuncNode->pfnMsgCallback = pCreateInfo->pfnCallback;
90 pNewDbgFuncNode->msgFlags = pCreateInfo->flags;
91 pNewDbgFuncNode->pUserData = pCreateInfo->pUserData;
92 pNewDbgFuncNode->pNext = inst->DbgFunctionHead;
93 inst->DbgFunctionHead = pNewDbgFuncNode;
94
95 return VK_SUCCESS;
96}
97
Courtney Goeltzenleuchter05854bf2015-11-30 12:13:14 -070098static VKAPI_ATTR VkResult VKAPI_CALL debug_report_CreateDebugReportCallback(
Jon Ashburn23d36b12016-02-02 17:47:28 -070099 VkInstance instance, VkDebugReportCallbackCreateInfoEXT *pCreateInfo,
100 VkAllocationCallbacks *pAllocator, VkDebugReportCallbackEXT *pCallback) {
Jon Ashburne0e64572015-09-30 12:56:42 -0600101 struct loader_instance *inst = loader_get_instance(instance);
Jon Ashburn6301a0f2015-05-29 13:15:39 -0600102 loader_platform_thread_lock_mutex(&loader_lock);
Jon Ashburn23d36b12016-02-02 17:47:28 -0700103 VkResult result = inst->disp->CreateDebugReportCallbackEXT(
104 instance, pCreateInfo, pAllocator, pCallback);
Courtney Goeltzenleuchterf579fa62015-06-10 17:39:03 -0600105 if (result == VK_SUCCESS) {
Jon Ashburn23d36b12016-02-02 17:47:28 -0700106 result = util_CreateDebugReportCallback(inst, pCreateInfo, pAllocator,
107 *pCallback);
Courtney Goeltzenleuchterf579fa62015-06-10 17:39:03 -0600108 }
Jon Ashburn6301a0f2015-05-29 13:15:39 -0600109 loader_platform_thread_unlock_mutex(&loader_lock);
Courtney Goeltzenleuchterf579fa62015-06-10 17:39:03 -0600110 return result;
111}
112
Courtney Goeltzenleuchter822e8d72015-11-30 15:28:25 -0700113// Utility function to handle reporting
Jon Ashburn23d36b12016-02-02 17:47:28 -0700114VkBool32 util_DebugReportMessage(const struct loader_instance *inst,
115 VkFlags msgFlags,
116 VkDebugReportObjectTypeEXT objectType,
117 uint64_t srcObject, size_t location,
118 int32_t msgCode, const char *pLayerPrefix,
119 const char *pMsg) {
Courtney Goeltzenleuchter822e8d72015-11-30 15:28:25 -0700120 VkBool32 bail = false;
121 VkLayerDbgFunctionNode *pTrav = inst->DbgFunctionHead;
122 while (pTrav) {
123 if (pTrav->msgFlags & msgFlags) {
Jon Ashburn23d36b12016-02-02 17:47:28 -0700124 if (pTrav->pfnMsgCallback(msgFlags, objectType, srcObject, location,
125 msgCode, pLayerPrefix, pMsg,
126 pTrav->pUserData)) {
Courtney Goeltzenleuchter822e8d72015-11-30 15:28:25 -0700127 bail = true;
128 }
129 }
130 pTrav = pTrav->pNext;
131 }
132
133 return bail;
134}
135
Jon Ashburn23d36b12016-02-02 17:47:28 -0700136void util_DestroyDebugReportCallback(struct loader_instance *inst,
137 VkDebugReportCallbackEXT callback,
138 const VkAllocationCallbacks *pAllocator) {
Courtney Goeltzenleuchter82887272015-12-02 15:29:33 -0700139 VkLayerDbgFunctionNode *pTrav = inst->DbgFunctionHead;
140 VkLayerDbgFunctionNode *pPrev = pTrav;
141
142 while (pTrav) {
143 if (pTrav->msgCallback == callback) {
144 pPrev->pNext = pTrav->pNext;
145 if (inst->DbgFunctionHead == pTrav)
146 inst->DbgFunctionHead = pTrav->pNext;
147 if (pAllocator != NULL) {
148 pAllocator->pfnFree(pAllocator->pUserData, pTrav);
149 } else {
150 loader_heap_free(inst, pTrav);
151 }
152 break;
153 }
154 pPrev = pTrav;
155 pTrav = pTrav->pNext;
156 }
157}
Courtney Goeltzenleuchter822e8d72015-11-30 15:28:25 -0700158
Jon Ashburn23d36b12016-02-02 17:47:28 -0700159static VKAPI_ATTR void VKAPI_CALL
160debug_report_DestroyDebugReportCallback(VkInstance instance,
161 VkDebugReportCallbackEXT callback,
162 VkAllocationCallbacks *pAllocator) {
Jon Ashburne0e64572015-09-30 12:56:42 -0600163 struct loader_instance *inst = loader_get_instance(instance);
Jon Ashburn6301a0f2015-05-29 13:15:39 -0600164 loader_platform_thread_lock_mutex(&loader_lock);
Courtney Goeltzenleuchterf579fa62015-06-10 17:39:03 -0600165
Courtney Goeltzenleuchter7415d5a2015-12-09 15:48:16 -0700166 inst->disp->DestroyDebugReportCallbackEXT(instance, callback, pAllocator);
Courtney Goeltzenleuchterf579fa62015-06-10 17:39:03 -0600167
Courtney Goeltzenleuchter82887272015-12-02 15:29:33 -0700168 util_DestroyDebugReportCallback(inst, callback, pAllocator);
Courtney Goeltzenleuchterf579fa62015-06-10 17:39:03 -0600169
Jon Ashburn6301a0f2015-05-29 13:15:39 -0600170 loader_platform_thread_unlock_mutex(&loader_lock);
Courtney Goeltzenleuchterf579fa62015-06-10 17:39:03 -0600171}
172
Courtney Goeltzenleuchter822e8d72015-11-30 15:28:25 -0700173static VKAPI_ATTR void VKAPI_CALL debug_report_DebugReportMessage(
Jon Ashburn23d36b12016-02-02 17:47:28 -0700174 VkInstance instance, VkDebugReportFlagsEXT flags,
175 VkDebugReportObjectTypeEXT objType, uint64_t object, size_t location,
176 int32_t msgCode, const char *pLayerPrefix, const char *pMsg) {
Courtney Goeltzenleuchter822e8d72015-11-30 15:28:25 -0700177 struct loader_instance *inst = loader_get_instance(instance);
178
Jon Ashburn23d36b12016-02-02 17:47:28 -0700179 inst->disp->DebugReportMessageEXT(instance, flags, objType, object,
180 location, msgCode, pLayerPrefix, pMsg);
Courtney Goeltzenleuchter822e8d72015-11-30 15:28:25 -0700181}
Courtney Goeltzenleuchterf579fa62015-06-10 17:39:03 -0600182
183/*
184 * This is the instance chain terminator function
Courtney Goeltzenleuchter05854bf2015-11-30 12:13:14 -0700185 * for CreateDebugReportCallback
Courtney Goeltzenleuchterf579fa62015-06-10 17:39:03 -0600186 */
Tony Barbour1d2cd3f2015-07-03 10:33:54 -0600187
Jon Ashburn1530c342016-02-26 13:14:27 -0700188VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateDebugReportCallback(
Jon Ashburn23d36b12016-02-02 17:47:28 -0700189 VkInstance instance, const VkDebugReportCallbackCreateInfoEXT *pCreateInfo,
190 const VkAllocationCallbacks *pAllocator,
191 VkDebugReportCallbackEXT *pCallback) {
Courtney Goeltzenleuchter7415d5a2015-12-09 15:48:16 -0700192 VkDebugReportCallbackEXT *icd_info;
Courtney Goeltzenleuchterf579fa62015-06-10 17:39:03 -0600193 const struct loader_icd *icd;
Jon Ashburn23d36b12016-02-02 17:47:28 -0700194 struct loader_instance *inst = (struct loader_instance *)instance;
Karl Schultz2558bd32016-02-24 14:39:39 -0700195 VkResult res = VK_SUCCESS;
Courtney Goeltzenleuchterf579fa62015-06-10 17:39:03 -0600196 uint32_t storage_idx;
197
Courtney Goeltzenleuchter7415d5a2015-12-09 15:48:16 -0700198 icd_info = calloc(sizeof(VkDebugReportCallbackEXT), inst->total_icd_count);
Courtney Goeltzenleuchterf579fa62015-06-10 17:39:03 -0600199 if (!icd_info) {
200 return VK_ERROR_OUT_OF_HOST_MEMORY;
201 }
202
203 storage_idx = 0;
204 for (icd = inst->icds; icd; icd = icd->next) {
Courtney Goeltzenleuchter7415d5a2015-12-09 15:48:16 -0700205 if (!icd->CreateDebugReportCallbackEXT) {
Courtney Goeltzenleuchterf579fa62015-06-10 17:39:03 -0600206 continue;
207 }
208
Courtney Goeltzenleuchter7415d5a2015-12-09 15:48:16 -0700209 res = icd->CreateDebugReportCallbackEXT(
Jon Ashburn23d36b12016-02-02 17:47:28 -0700210 icd->instance, pCreateInfo, pAllocator, &icd_info[storage_idx]);
Courtney Goeltzenleuchterf579fa62015-06-10 17:39:03 -0600211
212 if (res != VK_SUCCESS) {
213 break;
214 }
215 storage_idx++;
216 }
217
218 /* roll back on errors */
219 if (icd) {
220 storage_idx = 0;
221 for (icd = inst->icds; icd; icd = icd->next) {
Chia-I Wue2fc5522015-10-26 20:04:44 +0800222 if (icd_info[storage_idx]) {
Courtney Goeltzenleuchter7415d5a2015-12-09 15:48:16 -0700223 icd->DestroyDebugReportCallbackEXT(
Jon Ashburn23d36b12016-02-02 17:47:28 -0700224 icd->instance, icd_info[storage_idx], pAllocator);
Courtney Goeltzenleuchterf579fa62015-06-10 17:39:03 -0600225 }
226 storage_idx++;
227 }
228
229 return res;
230 }
231
Courtney Goeltzenleuchter7415d5a2015-12-09 15:48:16 -0700232 *(VkDebugReportCallbackEXT **)pCallback = icd_info;
Courtney Goeltzenleuchterf579fa62015-06-10 17:39:03 -0600233
234 return VK_SUCCESS;
235}
236
237/*
238 * This is the instance chain terminator function
Courtney Goeltzenleuchter05854bf2015-11-30 12:13:14 -0700239 * for DestroyDebugReportCallback
Courtney Goeltzenleuchterf579fa62015-06-10 17:39:03 -0600240 */
Jon Ashburn23d36b12016-02-02 17:47:28 -0700241VKAPI_ATTR void VKAPI_CALL
Jon Ashburn1530c342016-02-26 13:14:27 -0700242terminator_DestroyDebugReportCallback(VkInstance instance,
243 VkDebugReportCallbackEXT callback,
244 const VkAllocationCallbacks *pAllocator) {
Courtney Goeltzenleuchterf579fa62015-06-10 17:39:03 -0600245 uint32_t storage_idx;
Courtney Goeltzenleuchter7415d5a2015-12-09 15:48:16 -0700246 VkDebugReportCallbackEXT *icd_info;
Courtney Goeltzenleuchterf579fa62015-06-10 17:39:03 -0600247 const struct loader_icd *icd;
Courtney Goeltzenleuchterf579fa62015-06-10 17:39:03 -0600248
Jon Ashburn23d36b12016-02-02 17:47:28 -0700249 struct loader_instance *inst = (struct loader_instance *)instance;
250 icd_info = *(VkDebugReportCallbackEXT **)&callback;
Courtney Goeltzenleuchterf579fa62015-06-10 17:39:03 -0600251 storage_idx = 0;
252 for (icd = inst->icds; icd; icd = icd->next) {
Chia-I Wue2fc5522015-10-26 20:04:44 +0800253 if (icd_info[storage_idx]) {
Courtney Goeltzenleuchter7415d5a2015-12-09 15:48:16 -0700254 icd->DestroyDebugReportCallbackEXT(
Jon Ashburn23d36b12016-02-02 17:47:28 -0700255 icd->instance, icd_info[storage_idx], pAllocator);
Courtney Goeltzenleuchterf579fa62015-06-10 17:39:03 -0600256 }
257 storage_idx++;
258 }
Courtney Goeltzenleuchterf579fa62015-06-10 17:39:03 -0600259}
260
Courtney Goeltzenleuchter822e8d72015-11-30 15:28:25 -0700261/*
262 * This is the instance chain terminator function
263 * for DebugReportMessage
264 */
Jon Ashburn23d36b12016-02-02 17:47:28 -0700265VKAPI_ATTR void VKAPI_CALL
Jon Ashburn1530c342016-02-26 13:14:27 -0700266terminator_DebugReportMessage(VkInstance instance, VkDebugReportFlagsEXT flags,
267 VkDebugReportObjectTypeEXT objType,
268 uint64_t object, size_t location, int32_t msgCode,
269 const char *pLayerPrefix, const char *pMsg) {
Courtney Goeltzenleuchter822e8d72015-11-30 15:28:25 -0700270 const struct loader_icd *icd;
Courtney Goeltzenleuchter82887272015-12-02 15:29:33 -0700271
Jon Ashburn23d36b12016-02-02 17:47:28 -0700272 struct loader_instance *inst = (struct loader_instance *)instance;
Courtney Goeltzenleuchter822e8d72015-11-30 15:28:25 -0700273
Courtney Goeltzenleuchter82887272015-12-02 15:29:33 -0700274 loader_platform_thread_lock_mutex(&loader_lock);
Courtney Goeltzenleuchter822e8d72015-11-30 15:28:25 -0700275 for (icd = inst->icds; icd; icd = icd->next) {
Courtney Goeltzenleuchter7415d5a2015-12-09 15:48:16 -0700276 if (icd->DebugReportMessageEXT != NULL) {
Jon Ashburn23d36b12016-02-02 17:47:28 -0700277 icd->DebugReportMessageEXT(icd->instance, flags, objType, object,
278 location, msgCode, pLayerPrefix, pMsg);
Courtney Goeltzenleuchter822e8d72015-11-30 15:28:25 -0700279 }
280 }
281
282 /*
283 * Now that all ICDs have seen the message, call the necessary callbacks.
Jon Ashburn23d36b12016-02-02 17:47:28 -0700284 * Ignoring "bail" return value as there is nothing to bail from at this
285 * point.
Courtney Goeltzenleuchter822e8d72015-11-30 15:28:25 -0700286 */
Courtney Goeltzenleuchter82887272015-12-02 15:29:33 -0700287
Jon Ashburn23d36b12016-02-02 17:47:28 -0700288 util_DebugReportMessage(inst, flags, objType, object, location, msgCode,
289 pLayerPrefix, pMsg);
Courtney Goeltzenleuchter82887272015-12-02 15:29:33 -0700290
291 loader_platform_thread_unlock_mutex(&loader_lock);
Courtney Goeltzenleuchter822e8d72015-11-30 15:28:25 -0700292}
293
Jon Ashburn23d36b12016-02-02 17:47:28 -0700294bool debug_report_instance_gpa(struct loader_instance *ptr_instance,
295 const char *name, void **addr) {
Jon Ashburn8a39efc2015-11-06 11:02:40 -0700296 // debug_report is currently advertised to be supported by the loader,
297 // so always return the entry points if name matches and it's enabled
Jon Ashburnf7a48db2015-10-01 12:03:17 -0600298 *addr = NULL;
Courtney Goeltzenleuchterf579fa62015-06-10 17:39:03 -0600299
Courtney Goeltzenleuchter7415d5a2015-12-09 15:48:16 -0700300 if (!strcmp("vkCreateDebugReportCallbackEXT", name)) {
Jon Ashburn23d36b12016-02-02 17:47:28 -0700301 *addr = ptr_instance->debug_report_enabled
302 ? (void *)debug_report_CreateDebugReportCallback
303 : NULL;
Jon Ashburnf7a48db2015-10-01 12:03:17 -0600304 return true;
305 }
Courtney Goeltzenleuchter7415d5a2015-12-09 15:48:16 -0700306 if (!strcmp("vkDestroyDebugReportCallbackEXT", name)) {
Jon Ashburn23d36b12016-02-02 17:47:28 -0700307 *addr = ptr_instance->debug_report_enabled
308 ? (void *)debug_report_DestroyDebugReportCallback
309 : NULL;
Jon Ashburnf7a48db2015-10-01 12:03:17 -0600310 return true;
311 }
Courtney Goeltzenleuchter7415d5a2015-12-09 15:48:16 -0700312 if (!strcmp("vkDebugReportMessageEXT", name)) {
Jon Ashburn23d36b12016-02-02 17:47:28 -0700313 *addr = ptr_instance->debug_report_enabled
314 ? (void *)debug_report_DebugReportMessage
315 : NULL;
Courtney Goeltzenleuchter822e8d72015-11-30 15:28:25 -0700316 return true;
317 }
Jon Ashburnf7a48db2015-10-01 12:03:17 -0600318 return false;
Courtney Goeltzenleuchterf579fa62015-06-10 17:39:03 -0600319}