diff options
Diffstat (limited to 'lldb/source/Expression/ClangExpression.cpp')
-rw-r--r-- | lldb/source/Expression/ClangExpression.cpp | 633 |
1 files changed, 633 insertions, 0 deletions
diff --git a/lldb/source/Expression/ClangExpression.cpp b/lldb/source/Expression/ClangExpression.cpp new file mode 100644 index 00000000000..f7742c685c7 --- /dev/null +++ b/lldb/source/Expression/ClangExpression.cpp @@ -0,0 +1,633 @@ +//===-- ClangExpression.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +#include <stdio.h> +#if HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif + +// C++ Includes +#include <cstdlib> +#include <string> +#include <map> + +// Other libraries and framework includes +#include "clang/AST/ASTContext.h" +#include "clang/AST/ExternalASTSource.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/Basic/Version.h" +#include "clang/CodeGen/ModuleBuilder.h" +#include "clang/Driver/CC1Options.h" +#include "clang/Driver/OptTable.h" +#include "clang/Frontend/CodeGenAction.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/CompilerInvocation.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Frontend/FrontendPluginRegistry.h" +#include "clang/Frontend/TextDiagnosticBuffer.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" +#include "clang/Frontend/VerifyDiagnosticsClient.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Sema/ParseAST.h" +#include "llvm/ExecutionEngine/ExecutionEngine.h" +#include "llvm/ExecutionEngine/JIT.h" +#include "llvm/Module.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/LLVMContext.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/System/DynamicLibrary.h" +#include "llvm/System/Host.h" +#include "llvm/System/Signals.h" +#include "llvm/Target/TargetSelect.h" + +// Project includes +#include "lldb/Expression/ClangExpression.h" +#include "lldb/Expression/ClangASTSource.h" +#include "lldb/Expression/ClangStmtVisitor.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Expression/RecordingMemoryManager.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" + +#define NO_RTTI +#include "lldb/Core/StreamString.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Core/dwarf.h" + + +using namespace lldb_private; +using namespace clang; +using namespace llvm; + +namespace clang { + +class AnalyzerOptions; +class CodeGenOptions; +class DependencyOutputOptions; +class DiagnosticOptions; +class FrontendOptions; +class HeaderSearchOptions; +class LangOptions; +class PreprocessorOptions; +class PreprocessorOutputOptions; +class TargetInfo; +class TargetOptions; + +} // end namespace clang + + + +//===----------------------------------------------------------------------===// +// Utility Methods +//===----------------------------------------------------------------------===// + +std::string GetBuiltinIncludePath(const char *Argv0) { + llvm::sys::Path P = + llvm::sys::Path::GetMainExecutable(Argv0, + (void*)(intptr_t) GetBuiltinIncludePath); + + if (!P.isEmpty()) { + P.eraseComponent(); // Remove /clang from foo/bin/clang + P.eraseComponent(); // Remove /bin from foo/bin + + // Get foo/lib/clang/<version>/include + P.appendComponent("lib"); + P.appendComponent("clang"); + P.appendComponent(CLANG_VERSION_STRING); + P.appendComponent("include"); + } + + return P.str(); +} + + +//===----------------------------------------------------------------------===// +// Main driver +//===----------------------------------------------------------------------===// + +void LLVMErrorHandler(void *UserData, const std::string &Message) { + Diagnostic &Diags = *static_cast<Diagnostic*>(UserData); + + Diags.Report(diag::err_fe_error_backend) << Message; + + // We cannot recover from llvm errors. + exit(1); +} + +static FrontendAction *CreateFrontendBaseAction(CompilerInstance &CI) { + using namespace clang::frontend; + + switch (CI.getFrontendOpts().ProgramAction) { + default: + llvm_unreachable("Invalid program action!"); + + case ASTDump: return new ASTDumpAction(); + case ASTPrint: return new ASTPrintAction(); + case ASTPrintXML: return new ASTPrintXMLAction(); + case ASTView: return new ASTViewAction(); + case DumpRawTokens: return new DumpRawTokensAction(); + case DumpTokens: return new DumpTokensAction(); + case EmitAssembly: return new EmitAssemblyAction(); + case EmitBC: return new EmitBCAction(); + case EmitHTML: return new HTMLPrintAction(); + case EmitLLVM: return new EmitLLVMAction(); + case EmitLLVMOnly: return new EmitLLVMOnlyAction(); + case EmitObj: return new EmitObjAction(); + case FixIt: return new FixItAction(); + case GeneratePCH: return new GeneratePCHAction(); + case GeneratePTH: return new GeneratePTHAction(); + case InheritanceView: return new InheritanceViewAction(); + case InitOnly: return new InitOnlyAction(); + case ParseNoop: return new ParseOnlyAction(); + case ParsePrintCallbacks: return new PrintParseAction(); + case ParseSyntaxOnly: return new SyntaxOnlyAction(); + + case PluginAction: { + if (CI.getFrontendOpts().ActionName == "help") { + llvm::errs() << "clang -cc1 plugins:\n"; + for (FrontendPluginRegistry::iterator it = + FrontendPluginRegistry::begin(), + ie = FrontendPluginRegistry::end(); + it != ie; ++it) + llvm::errs() << " " << it->getName() << " - " << it->getDesc() << "\n"; + return 0; + } + + for (FrontendPluginRegistry::iterator it = + FrontendPluginRegistry::begin(), ie = FrontendPluginRegistry::end(); + it != ie; ++it) { + if (it->getName() == CI.getFrontendOpts().ActionName) + return it->instantiate(); + } + + CI.getDiagnostics().Report(diag::err_fe_invalid_plugin_name) + << CI.getFrontendOpts().ActionName; + return 0; + } + + case PrintDeclContext: return new DeclContextPrintAction(); + case PrintPreprocessedInput: return new PrintPreprocessedAction(); + case RewriteMacros: return new RewriteMacrosAction(); + case RewriteObjC: return new RewriteObjCAction(); + case RewriteTest: return new RewriteTestAction(); + case RunAnalysis: return new AnalysisAction(); + case RunPreprocessorOnly: return new PreprocessOnlyAction(); + } +} + +//---------------------------------------------------------------------- +// ClangExpression constructor +//---------------------------------------------------------------------- +ClangExpression::ClangExpression(const char *target_triple, + ClangExpressionDeclMap *decl_map) : + m_target_triple (), + m_jit_mm_ptr (NULL), + m_code_generator_ptr (NULL), + m_decl_map (decl_map) +{ + if (target_triple && target_triple[0]) + m_target_triple = target_triple; + else + m_target_triple = llvm::sys::getHostTriple(); +} + + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +ClangExpression::~ClangExpression() +{ + if (m_code_generator_ptr && !m_execution_engine.get()) + delete m_code_generator_ptr; +} + +bool +ClangExpression::CreateCompilerInstance (bool &IsAST) +{ + // Initialize targets first, so that --version shows registered targets. + static struct InitializeLLVM { + InitializeLLVM() { + llvm::InitializeAllTargets(); + llvm::InitializeAllAsmPrinters(); + } + } InitializeLLVM; + + // 1. Create a new compiler instance. + m_clang_ap.reset(new CompilerInstance()); + m_clang_ap->setLLVMContext(new LLVMContext()); + + // 2. Set options. + + // Parse expressions as Objective C++ regardless of context. + // Our hook into Clang's lookup mechanism only works in C++. + m_clang_ap->getLangOpts().CPlusPlus = true; + m_clang_ap->getLangOpts().ObjC1 = true; + + // Disable some warnings. + m_clang_ap->getDiagnosticOpts().Warnings.push_back("no-unused-value"); + + // Set the target triple. + m_clang_ap->getTargetOpts().Triple = m_target_triple; + + // 3. Set up various important bits of infrastructure. + + m_clang_ap->createDiagnostics(0, 0); + m_clang_ap->getLangOpts().CPlusPlus = true; + + // Create the target instance. + m_clang_ap->setTarget(TargetInfo::CreateTargetInfo(m_clang_ap->getDiagnostics(), + m_clang_ap->getTargetOpts())); + if (!m_clang_ap->hasTarget()) + { + m_clang_ap.reset(); + return false; + } + + // Inform the target of the language options + // + // FIXME: We shouldn't need to do this, the target should be immutable once + // created. This complexity should be lifted elsewhere. + m_clang_ap->getTarget().setForcedLangOptions(m_clang_ap->getLangOpts()); + + return m_clang_ap.get(); +} + +Mutex & +ClangExpression::GetClangMutex () +{ + static Mutex g_clang_mutex(Mutex::eMutexTypeRecursive); // Control access to the clang compiler + return g_clang_mutex; +} + + +clang::ASTContext * +ClangExpression::GetASTContext () +{ + CompilerInstance *compiler_instance = GetCompilerInstance(); + if (compiler_instance) + return &compiler_instance->getASTContext(); + return NULL; +} + +unsigned +ClangExpression::ParseExpression (const char *expr_text, Stream &stream) +{ + // HACK: for now we have to make a function body around our expression + // since there is no way to parse a single expression line in LLVM/Clang. + std::string func_expr("void ___clang_expr()\n{\n\t"); + func_expr.append(expr_text); + func_expr.append(";\n}"); + return ParseBareExpression (func_expr, stream); + +} + +unsigned +ClangExpression::ParseBareExpression (llvm::StringRef expr_text, Stream &stream) +{ + Mutex::Locker locker(GetClangMutex ()); + + TextDiagnosticBuffer text_diagnostic_buffer; + + bool IsAST = false; + if (!CreateCompilerInstance (IsAST)) + { + stream.Printf("error: couldn't create compiler instance\n"); + return 1; + } + + // This code is matched below by a setClient to NULL. + // We cannot return out of this code without doing that. + m_clang_ap->getDiagnostics().setClient(&text_diagnostic_buffer); + text_diagnostic_buffer.FlushDiagnostics (m_clang_ap->getDiagnostics()); + + MemoryBuffer *memory_buffer = MemoryBuffer::getMemBufferCopy(expr_text, __FUNCTION__); + + if (!m_clang_ap->hasSourceManager()) + m_clang_ap->createSourceManager(); + + m_clang_ap->createFileManager(); + m_clang_ap->createPreprocessor(); + + // Build the ASTContext. Most of this we inherit from the + // CompilerInstance, but we also want to give the context + // an ExternalASTSource. + SelectorTable selector_table; + std::auto_ptr<Builtin::Context> builtin_ap(new Builtin::Context(m_clang_ap->getTarget())); + ASTContext *Context = new ASTContext(m_clang_ap->getLangOpts(), + m_clang_ap->getSourceManager(), + m_clang_ap->getTarget(), + m_clang_ap->getPreprocessor().getIdentifierTable(), + selector_table, + *builtin_ap.get()); + + llvm::OwningPtr<ExternalASTSource> ASTSource(new ClangASTSource(*Context, *m_decl_map)); + + if (m_decl_map) + { + Context->setExternalSource(ASTSource); + } + + m_clang_ap->setASTContext(Context); + + FileID memory_buffer_file_id = m_clang_ap->getSourceManager().createMainFileIDForMemBuffer (memory_buffer); + std::string module_name("test_func"); + text_diagnostic_buffer.BeginSourceFile(m_clang_ap->getLangOpts(), &m_clang_ap->getPreprocessor()); + + if (m_code_generator_ptr) + delete m_code_generator_ptr; + + m_code_generator_ptr = CreateLLVMCodeGen(m_clang_ap->getDiagnostics(), + module_name, + m_clang_ap->getCodeGenOpts(), + m_clang_ap->getLLVMContext()); + + + // - CodeGeneration ASTConsumer (include/clang/ModuleBuilder.h), which will be passed in when you call... + // - Call clang::ParseAST (in lib/Sema/ParseAST.cpp) to parse the buffer. The CodeGenerator will generate code for __dbg_expr. + // - Once ParseAST completes, you can grab the llvm::Module from the CodeGenerator, which will have an llvm::Function you can hand off to the JIT. + ParseAST(m_clang_ap->getPreprocessor(), m_code_generator_ptr, m_clang_ap->getASTContext()); + + text_diagnostic_buffer.EndSourceFile(); + + //compiler_instance->getASTContext().getTranslationUnitDecl()->dump(); + + //if (compiler_instance->getFrontendOpts().ShowStats) { + // compiler_instance->getFileManager().PrintStats(); + // fprintf(stderr, "\n"); + //} + + // This code resolves the setClient above. + m_clang_ap->getDiagnostics().setClient(0); + + TextDiagnosticBuffer::const_iterator diag_iterator; + + int num_errors = 0; + +#ifdef COUNT_WARNINGS_AND_ERRORS + int num_warnings = 0; + + for (diag_iterator = text_diagnostic_buffer.warn_begin(); + diag_iterator != text_diagnostic_buffer.warn_end(); + ++diag_iterator) + num_warnings++; + + for (diag_iterator = text_diagnostic_buffer.err_begin(); + diag_iterator != text_diagnostic_buffer.err_end(); + ++diag_iterator) + num_errors++; + + if (num_warnings || num_errors) + { + if (num_warnings) + stream.Printf("%u warning%s%s", num_warnings, (num_warnings == 1 ? "" : "s"), (num_errors ? " and " : "")); + if (num_errors) + stream.Printf("%u error%s", num_errors, (num_errors == 1 ? "" : "s")); + stream.Printf("\n"); + } +#endif + + for (diag_iterator = text_diagnostic_buffer.warn_begin(); + diag_iterator != text_diagnostic_buffer.warn_end(); + ++diag_iterator) + stream.Printf("warning: %s\n", (*diag_iterator).second.c_str()); + + num_errors = 0; + + for (diag_iterator = text_diagnostic_buffer.err_begin(); + diag_iterator != text_diagnostic_buffer.err_end(); + ++diag_iterator) + { + num_errors++; + stream.Printf("error: %s\n", (*diag_iterator).second.c_str()); + } + + return num_errors; +} + + +static FrontendAction * +CreateFrontendAction(CompilerInstance &CI) +{ + // Create the underlying action. + FrontendAction *Act = CreateFrontendBaseAction(CI); + if (!Act) + return 0; + + // If there are any AST files to merge, create a frontend action + // adaptor to perform the merge. + if (!CI.getFrontendOpts().ASTMergeFiles.empty()) + Act = new ASTMergeAction(Act, &CI.getFrontendOpts().ASTMergeFiles[0], + CI.getFrontendOpts().ASTMergeFiles.size()); + + return Act; +} + + +unsigned +ClangExpression::ConvertExpressionToDWARF (ClangExpressionVariableList& expr_local_variable_list, + StreamString &dwarf_opcode_strm) +{ + CompilerInstance *compiler_instance = GetCompilerInstance(); + + DeclarationName hack_func_name(&compiler_instance->getASTContext().Idents.get("___clang_expr")); + DeclContext::lookup_result result = compiler_instance->getASTContext().getTranslationUnitDecl()->lookup(hack_func_name); + + if (result.first != result.second) + { + Decl *decl = *result.first; + Stmt *decl_stmt = decl->getBody(); + if (decl_stmt) + { + ClangStmtVisitor visitor(compiler_instance->getASTContext(), expr_local_variable_list, m_decl_map, dwarf_opcode_strm); + + visitor.Visit (decl_stmt); + } + } + return 0; +} + +bool +ClangExpression::JITFunction (const ExecutionContext &exc_context, const char *name) +{ + + llvm::Module *module = m_code_generator_ptr->GetModule(); + + if (module) + { + std::string error; + + if (m_jit_mm_ptr == NULL) + m_jit_mm_ptr = new RecordingMemoryManager(); + + //llvm::InitializeNativeTarget(); + if (m_execution_engine.get() == 0) + m_execution_engine.reset(llvm::ExecutionEngine::createJIT (module, &error, m_jit_mm_ptr)); + m_execution_engine->DisableLazyCompilation(); + llvm::Function *function = module->getFunction (llvm::StringRef (name)); + + // We don't actually need the function pointer here, this just forces it to get resolved. + void *fun_ptr = m_execution_engine->getPointerToFunction(function); + // Note, you probably won't get here on error, since the LLVM JIT tends to just + // exit on error at present... So be careful. + if (fun_ptr == 0) + return false; + m_jitted_functions.push_back(ClangExpression::JittedFunction(name, (lldb::addr_t) fun_ptr)); + + } + return true; +} + +bool +ClangExpression::WriteJITCode (const ExecutionContext &exc_context) +{ + if (m_jit_mm_ptr == NULL) + return false; + + if (exc_context.process == NULL) + return false; + + // Look over the regions allocated for the function compiled. The JIT + // tries to allocate the functions & stubs close together, so we should try to + // write them that way too... + // For now I only write functions with no stubs, globals, exception tables, + // etc. So I only need to write the functions. + + size_t size = 0; + std::map<uint8_t *, uint8_t *>::iterator fun_pos, fun_end = m_jit_mm_ptr->m_functions.end(); + for (fun_pos = m_jit_mm_ptr->m_functions.begin(); fun_pos != fun_end; fun_pos++) + { + size += (*fun_pos).second - (*fun_pos).first; + } + + Error error; + lldb::addr_t target_addr = exc_context.process->AllocateMemory (size, lldb::ePermissionsReadable|lldb::ePermissionsExecutable, error); + + if (target_addr == LLDB_INVALID_ADDRESS) + return false; + + lldb::addr_t cursor = target_addr; + for (fun_pos = m_jit_mm_ptr->m_functions.begin(); fun_pos != fun_end; fun_pos++) + { + lldb::addr_t lstart = (lldb::addr_t) (*fun_pos).first; + lldb::addr_t lend = (lldb::addr_t) (*fun_pos).second; + size_t size = lend - lstart; + exc_context.process->WriteMemory(cursor, (void *) lstart, size, error); + m_jit_mm_ptr->AddToLocalToRemoteMap (lstart, size, cursor); + cursor += size; + } + + std::vector<JittedFunction>::iterator pos, end = m_jitted_functions.end(); + + for (pos = m_jitted_functions.begin(); pos != end; pos++) + { + (*pos).m_remote_addr = m_jit_mm_ptr->GetRemoteAddressForLocal ((*pos).m_local_addr); + } + return true; +} + +lldb::addr_t +ClangExpression::GetFunctionAddress (const char *name) +{ + std::vector<JittedFunction>::iterator pos, end = m_jitted_functions.end(); + + for (pos = m_jitted_functions.begin(); pos < end; pos++) + { + if (strcmp ((*pos).m_name.c_str(), name) == 0) + return (*pos).m_remote_addr; + } + return LLDB_INVALID_ADDRESS; +} + +unsigned +ClangExpression::Compile() +{ + Mutex::Locker locker(GetClangMutex ()); + bool IsAST = false; + + if (CreateCompilerInstance(IsAST)) + { + // Validate/process some options + if (m_clang_ap->getHeaderSearchOpts().Verbose) + llvm::errs() << "clang-cc version " CLANG_VERSION_STRING + << " based upon " << PACKAGE_STRING + << " hosted on " << llvm::sys::getHostTriple() << "\n"; + + // Enforce certain implications. + if (!m_clang_ap->getFrontendOpts().ViewClassInheritance.empty()) + m_clang_ap->getFrontendOpts().ProgramAction = frontend::InheritanceView; +// if (!compiler_instance->getFrontendOpts().FixItSuffix.empty()) +// compiler_instance->getFrontendOpts().ProgramAction = frontend::FixIt; + + for (unsigned i = 0, e = m_clang_ap->getFrontendOpts().Inputs.size(); i != e; ++i) { + const std::string &InFile = m_clang_ap->getFrontendOpts().Inputs[i].second; + + // If we aren't using an AST file, setup the file and source managers and + // the preprocessor. + if (!IsAST) { + if (!i) { + // Create a file manager object to provide access to and cache the + // filesystem. + m_clang_ap->createFileManager(); + + // Create the source manager. + m_clang_ap->createSourceManager(); + } else { + // Reset the ID tables if we are reusing the SourceManager. + m_clang_ap->getSourceManager().clearIDTables(); + } + + // Create the preprocessor. + m_clang_ap->createPreprocessor(); + } + + llvm::OwningPtr<FrontendAction> Act(CreateFrontendAction(*m_clang_ap.get())); + if (!Act) + break; + + if (Act->BeginSourceFile(*m_clang_ap, InFile, IsAST)) { + Act->Execute(); + Act->EndSourceFile(); + } + } + + if (m_clang_ap->getDiagnosticOpts().ShowCarets) + { + unsigned NumWarnings = m_clang_ap->getDiagnostics().getNumWarnings(); + unsigned NumErrors = m_clang_ap->getDiagnostics().getNumErrors() - + m_clang_ap->getDiagnostics().getNumErrorsSuppressed(); + + if (NumWarnings || NumErrors) + { + if (NumWarnings) + fprintf (stderr, "%u warning%s%s", NumWarnings, (NumWarnings == 1 ? "" : "s"), (NumErrors ? " and " : "")); + if (NumErrors) + fprintf (stderr, "%u error%s", NumErrors, (NumErrors == 1 ? "" : "s")); + fprintf (stderr, " generated.\n"); + } + } + + if (m_clang_ap->getFrontendOpts().ShowStats) { + m_clang_ap->getFileManager().PrintStats(); + fprintf(stderr, "\n"); + } + + // Return the appropriate status when verifying diagnostics. + // + // FIXME: If we could make getNumErrors() do the right thing, we wouldn't need + // this. + if (m_clang_ap->getDiagnosticOpts().VerifyDiagnostics) + return static_cast<VerifyDiagnosticsClient&>(m_clang_ap->getDiagnosticClient()).HadErrors(); + + return m_clang_ap->getDiagnostics().getNumErrors(); + } + return 1; +} |