Running multiple actions with clang-interpreter without memory leak

classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Running multiple actions with clang-interpreter without memory leak

Florian Rival
Hi,

I'm using LLVM and Clang ( 2.9 ) in a program, and a memory leak
happens each time I'm launching a compilation process inside the
program.
I've made some tests with the Clang-interpreter example. I've added a
simple loop to make the compilation happens 500 times ( See below ).
When launching the modified example with a simple "Hello world" C file
as input, the memory consumption keeps growing ( reaching for examle
50 mb ram when the 500 compilation iterations are over. )

The documentation states that CompilerInstance::ExecuteAction assumes
that the user calls llvm_shutdown() "to ensure that any managed
objects are properly destroyed.". But if I add llvm_shutdown() just
after ExecuteAction(), the program is crashing at the next iteration.

How can I ensure that no memory is leaked during the process ?
I'm using LLVM/Clang in my software in a similar way, but with largest
sources files and as the compilation can be launched many times, the
software takes more and more memory until the OS kills it.

Here is the complete modified clang-interpreter example :

//******************************************************************************
#include <iostream>
#include "clang/CodeGen/CodeGenAction.h"
#include "clang/Driver/Compilation.h"
#include "clang/Driver/Driver.h"
#include "clang/Driver/Tool.h"
#include "clang/Frontend/CompilerInvocation.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/DiagnosticOptions.h"
#include "clang/Frontend/FrontendDiagnostic.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"

#include "llvm/Module.h"
#include "llvm/Config/config.h"
#include "llvm/ADT/OwningPtr.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Config/config.h"
#include "llvm/ExecutionEngine/ExecutionEngine.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/Path.h"
#include "llvm/Target/TargetSelect.h"
using namespace clang;
using namespace clang::driver;

// This function isn't referenced outside its translation unit, but it
// can't use the "static" keyword because its address is used for
// GetMainExecutable (since some platforms don't support taking the
// address of main, and some platforms can't implement GetMainExecutable
// without being given the address of a function in the main executable).
llvm::sys::Path GetExecutablePath(const char *Argv0) {
  // This just needs to be some symbol in the binary; C++ doesn't
  // allow taking the address of ::main however.
  void *MainAddr = (void*) (intptr_t) GetExecutablePath;
  return llvm::sys::Path::GetMainExecutable(Argv0, MainAddr);
}

static int Execute(llvm::Module *Mod, char * const *envp) {
  llvm::InitializeNativeTarget();

  std::string Error;
  llvm::OwningPtr<llvm::ExecutionEngine> EE(
    llvm::ExecutionEngine::createJIT(Mod, &Error));
  if (!EE) {
    llvm::errs() << "unable to make execution engine: " << Error << "\n";
    return 255;
  }

  llvm::Function *EntryFn = Mod->getFunction("main");
  if (!EntryFn) {
    llvm::errs() << "'main' function not found in module.\n";
    return 255;
  }

  // FIXME: Support passing arguments.
  std::vector<std::string> Args;
  Args.push_back(Mod->getModuleIdentifier());

  return EE->runFunctionAsMain(EntryFn, Args, envp);
}

int main(int argc, const char **argv, char * const *envp) {
  void *MainAddr = (void*) (intptr_t) GetExecutablePath;

  // Create and execute the frontend to generate an LLVM bitcode module.
  for(unsigned int i = 0;i<500;++i)
  {
          llvm::sys::Path Path = GetExecutablePath(argv[0]);
          TextDiagnosticPrinter *DiagClient =
                new TextDiagnosticPrinter(llvm::errs(), DiagnosticOptions());

          llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
          Diagnostic Diags(DiagID, DiagClient);
          Driver TheDriver(Path.str(), llvm::sys::getHostTriple(),
                                           "a.out", /*IsProduction=*/false, /*CXXIsProduction=*/false,
                                           Diags);
          TheDriver.setTitle("clang interpreter");

          // FIXME: This is a hack to try to force the driver to do something we can
          // recognize. We need to extend the driver library to support this use model
          // (basically, exactly one input, and the operation mode is hard wired).
          llvm::SmallVector<const char *, 16> Args(argv, argv + argc);
          Args.push_back("-fsyntax-only");
          llvm::OwningPtr<Compilation> C(TheDriver.BuildCompilation(Args.size(),
                                                                                                                                Args.data()));
          if (!C)
                return 0;

          // FIXME: This is copied from ASTUnit.cpp; simplify and eliminate.

          // We expect to get back exactly one command job, if we didn't something
          // failed. Extract that job from the compilation.
          const driver::JobList &Jobs = C->getJobs();
          if (Jobs.size() != 1 || !isa<driver::Command>(Jobs.begin())) {
                llvm::SmallString<256> Msg;
                llvm::raw_svector_ostream OS(Msg);
                C->PrintJob(OS, C->getJobs(), "; ", true);
                Diags.Report(diag::err_fe_expected_compiler_job) << OS.str();
                return 1;
          }

          const driver::Command *Cmd = cast<driver::Command>(*Jobs.begin());
          if (llvm::StringRef(Cmd->getCreator().getName()) != "clang") {
                Diags.Report(diag::err_fe_expected_clang_command);
                return 1;
          }

          // Initialize a compiler invocation object from the clang (-cc1) arguments.
          const driver::ArgStringList &CCArgs = Cmd->getArguments();
          llvm::OwningPtr<CompilerInvocation> CI(new CompilerInvocation);
          CompilerInvocation::CreateFromArgs(*CI,
                                                                                 const_cast<const char **>(CCArgs.data()),
                                                                                 const_cast<const char **>(CCArgs.data()) +
                                                                                   CCArgs.size(),
                                                                                 Diags);

          // Show the invocation, with -v.
          if (CI->getHeaderSearchOpts().Verbose) {
                llvm::errs() << "clang invocation:\n";
                C->PrintJob(llvm::errs(), C->getJobs(), "\n", true);
                llvm::errs() << "\n";
          }

          // FIXME: This is copied from cc1_main.cpp; simplify and eliminate.

          // Create a compiler instance to handle the actual work.
          CompilerInstance Clang;
          Clang.setInvocation(CI.take());

          // Create the compilers actual diagnostics engine.
          Clang.createDiagnostics(int(CCArgs.size()),const_cast<char**>(CCArgs.data()));
          if (!Clang.hasDiagnostics())
                return 1;

          // Infer the builtin include path if unspecified.
          if (Clang.getHeaderSearchOpts().UseBuiltinIncludes &&
                  Clang.getHeaderSearchOpts().ResourceDir.empty())
                Clang.getHeaderSearchOpts().ResourceDir =
                  CompilerInvocation::GetResourcesPath(argv[0], MainAddr);
       
       
          llvm::OwningPtr<CodeGenAction> Act(new EmitLLVMOnlyAction());
          if (!Clang.ExecuteAction(*Act))
                return 1;

          //llvm_shutdown(); //Make the program crash after the first iteration.
  }
       
  {
        std::cout << "500 actions ended";
        std::string pause;
        std::cin >> pause;
  }

  //I don't care about launching the compiled module.
  /*int Res = 255;
  if (llvm::Module *Module = Act->takeModule())
    Res = Execute(Module, envp);*/

  // Shutdown.
  llvm::llvm_shutdown();

  return 0;
}
//******************************************************************************

Thanks for any suggestion.

Florian Rival
_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-dev
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Running multiple actions with clang-interpreter without memory leak

Evgeny
This post has NOT been accepted by the mailing list yet.
There is no need in calling llvm_shutdown() after every ExecuteAction() as it is still written in source code annotation. Filtering out cc1 option -disable-free, which is set by default, eliminates huge memory leaks of ExecuteAction(). Allocated by ExecuteAction() memory is freed, BuryPointer() function is not called. Verified on clang 6.0.0svn. P.S. ToDo: 1. Misleading annotation of ExecuteAction() is worth to rewrite. 2. Misleading -disable-free option's description is worth to rewrite as well. 3. Make -disable-free option switchable.
Loading...