Browse Source

Fix last remaining known memory leak

* runProcess now properly manages the memory for arguments. It's also
a bit easier to call now that you can pass const char* arguments to it
* Changed some (when) to (unless)
HotReloadingState
Macoy Madson 9 months ago
parent
commit
05d21d6c97
5 changed files with 55 additions and 25 deletions
  1. +4
    -2
      doc/Debugging.org
  2. +4
    -4
      runtime/Macros.cake
  3. +8
    -11
      src/Evaluator.cpp
  4. +36
    -6
      src/RunProcess.cpp
  5. +3
    -2
      src/RunProcess.hpp

+ 4
- 2
doc/Debugging.org View File

@ -2,12 +2,14 @@
Cakelisp doesn't really have an interpreter. Cakelisp always generates C/C++ code to do meaningful work. This means the Cakelisp transpiler, macros, generators, and of course final code output can be debugged using a regular C/C++ debugger like GDB, LLDB, Or Visual Studio Debugger.
* Verbosity
I've sprinkled ~bool verbose~ flags throughout the code. Eventually I will add a proper command-line option for changing verbosity (TODO).
Run ~cakelisp --help~ to see what command-line arguments may be passed in to control verbosity. If Cakelisp is doing something you don't expect, it may help to turn on verbosity for the sub-system you expect may be at fault.
* GDB
This command should be run in order to tell GDB where the ~.so~ files you want to debug are located:
The following command may be run in order to tell GDB where the ~.so~ files you want to debug are located:
#+BEGIN_SRC sh
set solib-search-path ~/Development/code/repositories/cakelisp/
#+END_SRC
(adjust that path to where you installed cakelisp, of course).
By setting a breakpoint in already generated C++ code, you will then hit the breakpoint once the code is regenerated and loaded at transpiler runtime (a.k.a. compile-time code execution).

+ 4
- 4
runtime/Macros.cake View File

@ -47,7 +47,7 @@
;; Make sure the type is valid before outputting anything
(var type-output (<> std::vector StringOutput))
(var type-after-name-output (<> std::vector StringOutput))
(when (not (tokenizedCTypeToString_Recursive tokens type-index true type-output type-after-name-output))
(unless (tokenizedCTypeToString_Recursive tokens type-index true type-output type-after-name-output)
(return false))
(addModifierToStringOutput (on-call type-output back) StringOutMod_SpaceAfter)
@ -58,7 +58,7 @@
;; Evaluate name
(var expressionContext EvaluatorContext context)
(set (field expressionContext scope) EvaluatorScope_ExpressionsOnly)
(when (not (= 0 (EvaluateGenerate_Recursive environment expressionContext tokens name-index output)))
(unless (= 0 (EvaluateGenerate_Recursive environment expressionContext tokens name-index output))
(return false))
;; Yep, believe it or not, C typedefs have the length of the array after the new type name
@ -103,9 +103,9 @@
(addLangTokenOutput (field output source) StringOutMod_OpenBlock (addr invocation-token))
(var bodyContext EvaluatorContext context)
(set (field bodyContext scope) EvaluatorScope_Body)
(when (not (= 0 (EvaluateGenerateAll_Recursive
(unless (= 0 (EvaluateGenerateAll_Recursive
environment bodyContext tokens start-body-index
nullptr output)))
nullptr output))
(return false))
(addLangTokenOutput (field output source) StringOutMod_CloseBlock (addr invocation-token))


+ 8
- 11
src/Evaluator.cpp View File

@ -743,11 +743,9 @@ int BuildEvaluateReferences(EvaluatorEnvironment& environment, int& numErrorsOut
}
// TODO: Get arguments all the way from the top
// TODO: Memory leak
// If not null terminated, the call will fail
char* arguments[] = {fileToExec, strdup("-g"), strdup("-c"),
sourceOutputName, strdup("-o"), buildObjectName,
strdup("-Isrc/"), strdup("-fPIC"), nullptr};
const char* arguments[] = {fileToExec, "-g", "-c", sourceOutputName, "-o",
buildObjectName, "-Isrc/", "-fPIC", nullptr};
RunProcessArguments compileArguments = {};
compileArguments.fileToExecute = fileToExec;
compileArguments.arguments = arguments;
@ -792,13 +790,12 @@ int BuildEvaluateReferences(EvaluatorEnvironment& environment, int& numErrorsOut
char linkerExecutable[MAX_PATH_LENGTH] = {0};
PrintBuffer(linkerExecutable, "/usr/bin/clang++");
// TODO: Memory leak
char* arguments[] = {linkerExecutable,
strdup("-shared"),
strdup("-o"),
strdup(buildObject.dynamicLibraryPath.c_str()),
strdup(buildObject.buildObjectName.c_str()),
nullptr};
const char* arguments[] = {linkerExecutable,
"-shared",
"-o",
buildObject.dynamicLibraryPath.c_str(),
buildObject.buildObjectName.c_str(),
nullptr};
RunProcessArguments linkArguments = {};
linkArguments.fileToExecute = linkerExecutable;
linkArguments.arguments = arguments;


+ 36
- 6
src/RunProcess.cpp View File

@ -31,13 +31,13 @@ struct Subprocess
static std::vector<Subprocess> s_subprocesses;
// Never returns, if success
void systemExecute(const RunProcessArguments& arguments)
void systemExecute(const char* fileToExecute, char** arguments)
{
#ifdef UNIX
// pid_t pid;
execvp(arguments.fileToExecute, arguments.arguments);
execvp(fileToExecute, arguments);
perror("RunProcess execvp() error: ");
printf("Failed to execute %s\n", arguments.fileToExecute);
printf("Failed to execute %s\n", fileToExecute);
#endif
}
@ -58,7 +58,7 @@ int runProcess(const RunProcessArguments& arguments, int* statusOut)
if (log.processes)
{
printf("RunProcess command: ");
for (char** arg = arguments.arguments; *arg != nullptr; ++arg)
for (const char** arg = arguments.arguments; *arg != nullptr; ++arg)
{
printf("%s ", *arg);
}
@ -83,6 +83,7 @@ int runProcess(const RunProcessArguments& arguments, int* statusOut)
// Child
else if (pid == 0)
{
// Redirect std out and err to the pipes instead
if (dup2(pipeFileDescriptors[PipeWrite], STDOUT_FILENO) == -1 ||
dup2(pipeFileDescriptors[PipeWrite], STDERR_FILENO) == -1)
{
@ -91,7 +92,33 @@ int runProcess(const RunProcessArguments& arguments, int* statusOut)
}
// Only write
close(pipeFileDescriptors[PipeRead]);
systemExecute(arguments);
char** nonConstArguments = nullptr;
{
int numArgs = 0;
for (const char** arg = arguments.arguments; *arg != nullptr; ++arg)
++numArgs;
// Add one for null sentinel
nonConstArguments = new char*[numArgs + 1];
int i = 0;
for (const char** arg = arguments.arguments; *arg != nullptr; ++arg)
{
nonConstArguments[i] = strdup(*arg);
++i;
}
// Null sentinel
nonConstArguments[numArgs] = nullptr;
}
systemExecute(arguments.fileToExecute, nonConstArguments);
// This shouldn't happen unless the execution failed or soemthing
for (char** arg = nonConstArguments; *arg != nullptr; ++arg)
delete *arg;
delete[] nonConstArguments;
// A failed child should not flush parent files
_exit(EXIT_FAILURE); /* */
}
@ -100,7 +127,10 @@ int runProcess(const RunProcessArguments& arguments, int* statusOut)
{
// Only read
close(pipeFileDescriptors[PipeWrite]);
// printf("Created child process %d\n", pid);
if (log.processes)
printf("Created child process %d\n", pid);
s_subprocesses.push_back({statusOut, pid, pipeFileDescriptors[PipeRead]});
}


+ 3
- 2
src/RunProcess.hpp View File

@ -3,10 +3,11 @@
struct RunProcessArguments
{
const char* fileToExecute;
char** arguments;
const char** arguments;
};
int runProcess(const RunProcessArguments& arguments, int* statusOut);
typedef void (*SubprocessOnOutputFunc)(const char* subprocessOutput);
int runProcess(const RunProcessArguments& arguments, int* statusOut);
void waitForAllProcessesClosed(SubprocessOnOutputFunc onOutput);

Loading…
Cancel
Save