1/* 2 * Copyright 2017 Advanced Micro Devices, Inc. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * on the rights to use, copy, modify, merge, publish, distribute, sub 8 * license, and/or sell copies of the Software, and to permit persons to whom 9 * the Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice (including the next 12 * paragraph) shall be included in all copies or substantial portions of the 13 * Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 18 * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, 19 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 20 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 21 * USE OR OTHER DEALINGS IN THE SOFTWARE. 22 */ 23 24#include "u_log.h" 25 26#include "util/u_memory.h" 27#include "util/u_string.h" 28 29struct page_entry { 30 const struct u_log_chunk_type *type; 31 void *data; 32}; 33 34struct u_log_page { 35 struct page_entry *entries; 36 unsigned num_entries; 37 unsigned max_entries; 38}; 39 40struct u_log_auto_logger { 41 u_auto_log_fn *callback; 42 void *data; 43}; 44 45/** 46 * Initialize the given logging context. 47 */ 48void 49u_log_context_init(struct u_log_context *ctx) 50{ 51 memset(ctx, 0, sizeof(*ctx)); 52} 53 54/** 55 * Free all resources associated with the given logging context. 56 * 57 * Pages taken from the context via \ref u_log_new_page must be destroyed 58 * separately. 59 */ 60void 61u_log_context_destroy(struct u_log_context *ctx) 62{ 63 u_log_page_destroy(ctx->cur); 64 FREE(ctx->auto_loggers); 65 memset(ctx, 0, sizeof(*ctx)); 66} 67 68/** 69 * Add an auto logger. 70 * 71 * Auto loggers are called each time a chunk is added to the log. 72 */ 73void 74u_log_add_auto_logger(struct u_log_context *ctx, u_auto_log_fn *callback, 75 void *data) 76{ 77 struct u_log_auto_logger *new_auto_loggers = 78 REALLOC(ctx->auto_loggers, 79 sizeof(*new_auto_loggers) * ctx->num_auto_loggers, 80 sizeof(*new_auto_loggers) * (ctx->num_auto_loggers + 1)); 81 if (!new_auto_loggers) { 82 fprintf(stderr, "Gallium u_log: out of memory\n"); 83 return; 84 } 85 86 unsigned idx = ctx->num_auto_loggers++; 87 ctx->auto_loggers = new_auto_loggers; 88 ctx->auto_loggers[idx].callback = callback; 89 ctx->auto_loggers[idx].data = data; 90} 91 92/** 93 * Make sure that auto loggers have run. 94 */ 95void 96u_log_flush(struct u_log_context *ctx) 97{ 98 if (!ctx->num_auto_loggers) 99 return; 100 101 struct u_log_auto_logger *auto_loggers = ctx->auto_loggers; 102 unsigned num_auto_loggers = ctx->num_auto_loggers; 103 104 /* Prevent recursion. */ 105 ctx->num_auto_loggers = 0; 106 ctx->auto_loggers = NULL; 107 108 for (unsigned i = 0; i < num_auto_loggers; ++i) 109 auto_loggers[i].callback(auto_loggers[i].data, ctx); 110 111 assert(!ctx->num_auto_loggers); 112 ctx->num_auto_loggers = num_auto_loggers; 113 ctx->auto_loggers = auto_loggers; 114} 115 116static void str_print(void *data, FILE *stream) 117{ 118 fputs((char *)data, stream); 119} 120 121static const struct u_log_chunk_type str_chunk_type = { 122 .destroy = free, 123 .print = str_print, 124}; 125 126void 127u_log_printf(struct u_log_context *ctx, const char *fmt, ...) 128{ 129 va_list va; 130 char *str = NULL; 131 132 va_start(va, fmt); 133 int ret = vasprintf(&str, fmt, va); 134 va_end(va); 135 136 if (ret >= 0) { 137 u_log_chunk(ctx, &str_chunk_type, str); 138 } else { 139 fprintf(stderr, "Gallium u_log_printf: out of memory\n"); 140 } 141} 142 143/** 144 * Add a custom chunk to the log. 145 * 146 * type->destroy will be called as soon as \p data is no longer needed. 147 */ 148void 149u_log_chunk(struct u_log_context *ctx, const struct u_log_chunk_type *type, 150 void *data) 151{ 152 struct u_log_page *page = ctx->cur; 153 154 u_log_flush(ctx); 155 156 if (!page) { 157 ctx->cur = CALLOC_STRUCT(u_log_page); 158 page = ctx->cur; 159 if (!page) 160 goto out_of_memory; 161 } 162 163 if (page->num_entries >= page->max_entries) { 164 unsigned new_max_entries = MAX2(16, page->num_entries * 2); 165 struct page_entry *new_entries = REALLOC(page->entries, 166 page->max_entries * sizeof(*page->entries), 167 new_max_entries * sizeof(*page->entries)); 168 if (!new_entries) 169 goto out_of_memory; 170 171 page->entries = new_entries; 172 page->max_entries = new_max_entries; 173 } 174 175 page->entries[page->num_entries].type = type; 176 page->entries[page->num_entries].data = data; 177 page->num_entries++; 178 return; 179 180out_of_memory: 181 fprintf(stderr, "Gallium: u_log: out of memory\n"); 182} 183 184/** 185 * Convenience helper that starts a new page and prints the previous one. 186 */ 187void 188u_log_new_page_print(struct u_log_context *ctx, FILE *stream) 189{ 190 u_log_flush(ctx); 191 192 if (ctx->cur) { 193 u_log_page_print(ctx->cur, stream); 194 u_log_page_destroy(ctx->cur); 195 ctx->cur = NULL; 196 } 197} 198 199/** 200 * Return the current page from the logging context and start a new one. 201 * 202 * The caller is responsible for destroying the returned page. 203 */ 204struct u_log_page * 205u_log_new_page(struct u_log_context *ctx) 206{ 207 u_log_flush(ctx); 208 209 struct u_log_page *page = ctx->cur; 210 ctx->cur = NULL; 211 return page; 212} 213 214/** 215 * Free all data associated with \p page. 216 */ 217void 218u_log_page_destroy(struct u_log_page *page) 219{ 220 if (!page) 221 return; 222 223 for (unsigned i = 0; i < page->num_entries; ++i) { 224 if (page->entries[i].type->destroy) 225 page->entries[i].type->destroy(page->entries[i].data); 226 } 227 FREE(page->entries); 228 FREE(page); 229} 230 231/** 232 * Print the given page to \p stream. 233 */ 234void 235u_log_page_print(struct u_log_page *page, FILE *stream) 236{ 237 for (unsigned i = 0; i < page->num_entries; ++i) 238 page->entries[i].type->print(page->entries[i].data, stream); 239} 240