Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Lua: fix an indentation bug in debug logs (the stack crashed)
[simgrid.git] / src / bindings / lua / lua_utils.c
1 /* Copyright (c) 2010. The SimGrid Team.
2  * All rights reserved.                                                     */
3
4 /* This program is free software; you can redistribute it and/or modify it
5  * under the terms of the license (GNU LGPL) which comes with this package. */
6
7 /* SimGrid Lua helper functions                                             */
8
9 #include <lauxlib.h>
10 #include "lua_utils.h"
11 #include "xbt.h"
12 #include "xbt/log.h"
13
14 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(lua_utils, bindings, "Lua helper functions");
15
16 /**
17  * @brief Returns a string representation of a value in the Lua stack.
18  *
19  * This function is for debugging purposes.
20  * It always returns the same pointer.
21  *
22  * @param L the Lua state
23  * @param index index in the stack
24  * @return a string representation of the value at this index
25  */
26 const char* sglua_tostring(lua_State* L, int index) {
27
28   static char buff[64];
29
30   switch (lua_type(L, index)) {
31
32     case LUA_TNIL:
33       sprintf(buff, "nil");
34       break;
35
36     case LUA_TNUMBER:
37       sprintf(buff, "%.3f", lua_tonumber(L, index));
38       break;
39
40     case LUA_TBOOLEAN:
41       sprintf(buff, "%s", lua_toboolean(L, index) ? "true" : "false");
42       break;
43
44     case LUA_TSTRING:
45       snprintf(buff, 63, "'%s'", lua_tostring(L, index));
46       break;
47
48     case LUA_TFUNCTION:
49       if (lua_iscfunction(L, index)) {
50         sprintf(buff, "C-function");
51       }
52       else {
53         sprintf(buff, "function");
54       }
55       break;
56
57     case LUA_TTABLE:
58       sprintf(buff, "table(%p)", lua_topointer(L, index));
59       break;
60
61     case LUA_TLIGHTUSERDATA:
62     case LUA_TUSERDATA:
63       sprintf(buff, "userdata(%p)", lua_touserdata(L, index));
64       break;
65
66     case LUA_TTHREAD:
67       sprintf(buff, "thread");
68       break;
69   }
70   return buff;
71 }
72
73 /**
74  * @brief Returns a string representation of a key-value pair.
75  *
76  * This function is for debugging purposes.
77  * It always returns the same pointer.
78  *
79  * @param L the Lua state
80  * @param key_index index of the key
81  * @param value_index index of the value
82  * @return a string representation of the key-value pair
83  */
84 const char* sglua_keyvalue_tostring(lua_State* L, int key_index, int value_index) {
85
86   static char buff[64];
87   /* value_tostring also always returns the same pointer */
88   int len = snprintf(buff, 63, "[%s] -> ", sglua_tostring(L, key_index));
89   snprintf(buff + len, 63 - len, "%s", sglua_tostring(L, value_index));
90   return buff;
91 }
92
93 /**
94  * @brief Returns a string composed of the specified number of spaces.
95  *
96  * This function can be used to indent strings for debugging purposes.
97  * It always returns the same pointer.
98  *
99  * @param length length of the string
100  * @return a string of this length with only spaces
101  */
102 const char* sglua_get_spaces(int length) {
103
104   static char spaces[128];
105
106   xbt_assert(length >= 0 && length < 128,
107       "Invalid indentation length: %d", length);
108   if (length != 0) {
109     memset(spaces, ' ', length);
110   }
111   spaces[length] = '\0';
112   return spaces;
113 }
114
115 /**
116  * @brief Dumps the Lua stack if debug logs are enabled.
117  * @param msg a message to print
118  * @param L a Lua state
119  */
120 void sglua_stack_dump(const char* msg, lua_State* L)
121 {
122   if (XBT_LOG_ISENABLED(lua_utils, xbt_log_priority_debug)) {
123     char buff[2048];
124     char* p = buff;
125     int i;
126     int top = lua_gettop(L);
127
128     //if (1) return;
129
130     fflush(stdout);
131
132     p[0] = '\0';
133     for (i = 1; i <= top; i++) {  /* repeat for each level */
134
135       p += sprintf(p, "%s", sglua_tostring(L, i));
136       p += sprintf(p, " ");       /* put a separator */
137     }
138     XBT_DEBUG("%s%s", msg, buff);
139   }
140 }
141
142 /**
143  * \brief Like luaL_checkudata, with additional debug logs.
144  *
145  * This function is for debugging purposes only.
146  *
147  * \param L a lua state
148  * \param ud index of the userdata to check in the stack
149  * \param tname key of the metatable of this userdata in the registry
150  */
151 void* sglua_checkudata_debug(lua_State* L, int ud, const char* tname)
152 {
153   XBT_DEBUG("Checking the userdata: ud = %d", ud);
154   sglua_stack_dump("my_checkudata: ", L);
155   void* p = lua_touserdata(L, ud);
156   lua_getfield(L, LUA_REGISTRYINDEX, tname);
157   const void* correct_mt = lua_topointer(L, -1);
158
159   int has_mt = lua_getmetatable(L, ud);
160   XBT_DEBUG("Checking the userdata: has metatable ? %d", has_mt);
161   const void* actual_mt = NULL;
162   if (has_mt) {
163     actual_mt = lua_topointer(L, -1);
164     lua_pop(L, 1);
165   }
166   XBT_DEBUG("Checking the task's metatable: expected %p, found %p", correct_mt, actual_mt);
167   sglua_stack_dump("my_checkudata: ", L);
168
169   if (p == NULL || !lua_getmetatable(L, ud) || !lua_rawequal(L, -1, -2))
170     luaL_typerror(L, ud, tname);
171   lua_pop(L, 2);
172   return p;
173 }
174
175 /**
176  * @brief Writes the specified data into a memory buffer.
177  *
178  * This function is a valid lua_Writer that writes into a memory buffer passed
179  * as userdata.
180  *
181  * @param L a lua state
182  * @param source some data
183  * @param sz number of bytes of data
184  * @param user_data the memory buffer to write
185  */
186 int sglua_memory_writer(lua_State* L, const void* source, size_t size,
187     void* userdata) {
188
189   sglua_buffer_t buffer = (sglua_buffer_t) userdata;
190   while (buffer->capacity < buffer->size + size) {
191     buffer->capacity *= 2;
192     buffer->data = xbt_realloc(buffer->data, buffer->capacity);
193   }
194   memcpy(buffer->data + buffer->size, source, size);
195   buffer->size += size;
196
197   return 0;
198 }