Logo AND Algorithmique Numérique Distribuée

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