Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Merge branch 'oldstyle_element_set'
[simgrid.git] / src / bindings / lua / lua_debug.cpp
1 /* Copyright (c) 2010-2016. 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
8 /*
9  * This file contains functions that aid users to debug their lua scripts; for instance,
10  * tables can be easily output and values are represented in a human-readable way. (For instance,
11  * a NULL value becomes the string "nil").
12  *
13  */
14  /* SimGrid Lua debug functions                                             */
15 extern "C" {
16 #include <lauxlib.h>
17 }
18 #include "lua_utils.h"
19 #include "xbt.h"
20
21 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(lua_debug, bindings, "Lua helper functions");
22
23 /**
24  * @brief Returns a string representation of a value in the Lua stack.
25  *
26  * This function is for debugging purposes.
27  * It always returns the same pointer.
28  *
29  * @param L the Lua state
30  * @param index index in the stack
31  * @return a string representation of the value at this index
32  */
33 const char* sglua_tostring(lua_State* L, int index) {
34
35   static char buff[64];
36
37   switch (lua_type(L, index)) {
38
39     case LUA_TNIL:
40       sprintf(buff, "nil");
41       break;
42
43     case LUA_TNUMBER: 
44       sprintf(buff, "%.3f", lua_tonumber(L, index));
45       break;
46
47     case LUA_TBOOLEAN:
48       sprintf(buff, "%s", lua_toboolean(L, index) ? "true" : "false");
49       break;
50
51     case LUA_TSTRING:
52       snprintf(buff, 63, "'%s'", lua_tostring(L, index));
53       break;
54
55     case LUA_TFUNCTION:
56       if (lua_iscfunction(L, index)) {
57         sprintf(buff, "C-function");
58       }
59       else {
60         sprintf(buff, "function");
61       }
62       break;
63
64     case LUA_TTABLE:
65       sprintf(buff, "table(%p)", lua_topointer(L, index));
66       break;
67
68     case LUA_TLIGHTUSERDATA:
69     case LUA_TUSERDATA:
70       sprintf(buff, "userdata(%p)", lua_touserdata(L, index));
71       break;
72
73     case LUA_TTHREAD:
74       sprintf(buff, "thread");
75       break;
76   }
77   return buff;
78 }
79
80 static int sglua_dump_table(lua_State* L) {
81   int argc = lua_gettop(L);
82
83   for (int i = 1; i < argc; i++) {
84     if (lua_istable(L, i)) {
85       lua_pushnil(L); /* table nil */
86
87       //lua_next pops the topmost element from the stack and 
88       //gets the next pair from the table
89       while (lua_next(L, -1)) { /* table key val  */
90         // we need to copy here, as a cast from "Number" to "String"
91         // could happen in Lua.
92         // see remark in the lua manual, function "lua_tolstring"
93         // http://www.lua.org/manual/5.3/manual.html#lua_tolstring
94
95         lua_pushvalue(L, -2); /* table key val key */
96
97         const char *key = lua_tostring(L, -1); /* table key val */
98         const char *val = lua_tostring(L, -1); /* table key     */
99
100         XBT_DEBUG("%s => %s", key, val);
101       }
102
103       lua_settop(L, argc); // Remove everything except the initial arguments
104     }
105   }
106
107   return 0;
108 }
109
110 /**
111  * @brief Returns a string composed of the specified number of spaces.
112  *
113  * This function can be used to indent strings for debugging purposes.
114  * It always returns the same pointer.
115  *
116  * @param length length of the string
117  * @return a string of this length with only spaces
118  */
119 const char* sglua_get_spaces(int length) {
120
121   static char spaces[128];
122
123   xbt_assert(length >= 0 && length < 128,
124       "Invalid indentation length: %d", length);
125   if (length != 0) {
126     memset(spaces, ' ', length);
127   }
128   spaces[length] = '\0';
129   return spaces;
130 }
131
132 /**
133  * @brief Returns a string representation of a key-value pair.
134  *
135  * It always returns the same pointer.
136  *
137  * @param L the Lua state
138  * @param key_index index of the key (in the lua stack)
139  * @param value_index index of the value (in the lua stack)
140  * @return a string representation of the key-value pair
141  */
142 const char* sglua_keyvalue_tostring(lua_State* L, int key_index, int value_index) {
143
144   static char buff[64];
145   /* value_tostring also always returns the same pointer */
146   int len = snprintf(buff, 63, "[%s] -> ", sglua_tostring(L, key_index));
147   snprintf(buff + len, 63 - len, "%s", sglua_tostring(L, value_index));
148   return buff;
149 }
150
151 /**
152  * @brief Dumps the Lua stack if debug logs are enabled.
153  * @param msg a message to print
154  * @param L a Lua state
155  */
156 void sglua_stack_dump(lua_State* L, const char* msg)
157 {
158   if (XBT_LOG_ISENABLED(lua_debug, xbt_log_priority_debug)) {
159     char buff[2048];
160     char* p = buff;
161     int top = lua_gettop(L);
162
163     fflush(stdout);
164
165     p[0] = '\0';
166     for (int i = 1; i <= top; i++) {  /* repeat for each level */
167       p += sprintf(p, "%s ", sglua_tostring(L, i));
168     }
169
170     XBT_DEBUG("%s%s", msg, buff);
171   }
172 }
173
174 /**
175  * \brief Like luaL_checkudata, with additional debug logs.
176  *
177  * This function is for debugging purposes only.
178  *
179  * \param L a lua state
180  * \param ud index of the userdata to check in the stack
181  * \param tname key of the metatable of this userdata in the registry
182  */
183 void* sglua_checkudata_debug(lua_State* L, int ud, const char* tname)
184 {
185   XBT_DEBUG("Checking the userdata: ud = %d", ud);
186   sglua_stack_dump(L, "my_checkudata: ");
187   void* p = lua_touserdata(L, ud);
188   lua_getfield(L, LUA_REGISTRYINDEX, tname);
189   const void* correct_mt = lua_topointer(L, -1);
190
191   int has_mt = lua_getmetatable(L, ud);
192   XBT_DEBUG("Checking the userdata: has metatable ? %d", has_mt);
193   const void* actual_mt = NULL;
194   if (has_mt) {
195     actual_mt = lua_topointer(L, -1);
196     lua_pop(L, 1);
197   }
198   XBT_DEBUG("Checking the task's metatable: expected %p, found %p", correct_mt, actual_mt);
199   sglua_stack_dump(L, "my_checkudata: ");
200
201   if (p == NULL || !lua_getmetatable(L, ud) || !lua_rawequal(L, -1, -2))
202     XBT_ERROR("Error: Userdata is NULL, couldn't find metatable or top of stack does not equal element below it.");
203   lua_pop(L, 2);
204   return p;
205 }
206
207 /**
208  * @brief Writes the specified data into a memory buffer.
209  *
210  * This function is a valid lua_Writer that writes into a memory buffer passed
211  * as userdata.
212  *
213  * @param L a lua state
214  * @param source some data
215  * @param sz number of bytes of data
216  * @param user_data the memory buffer to write
217  */
218 int sglua_memory_writer(lua_State* L, const void* source, size_t size,
219     void* userdata) {
220
221   sglua_buffer_t buffer = (sglua_buffer_t) userdata;
222   while (buffer->capacity < buffer->size + size) {
223     buffer->capacity *= 2;
224     buffer->data = (char*)xbt_realloc(buffer->data, buffer->capacity);
225   }
226   memcpy(buffer->data + buffer->size, source, size);
227   buffer->size += size;
228
229   return 0;
230 }