Logo AND Algorithmique Numérique Distribuée

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