summaryrefslogtreecommitdiff
path: root/source/Symbol/CompileUnit.cpp
blob: 6b98d48d326dd741328b791216ddfb3d54657223 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
//===-- CompileUnit.cpp -----------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "lldb/Symbol/CompileUnit.h"
#include "lldb/Core/Module.h"
#include "lldb/Symbol/LineTable.h"
#include "lldb/Symbol/SymbolVendor.h"
#include "lldb/Symbol/VariableList.h"
#include "lldb/Target/Language.h"

using namespace lldb;
using namespace lldb_private;

CompileUnit::CompileUnit(const lldb::ModuleSP &module_sp, void *user_data,
                         const char *pathname, const lldb::user_id_t cu_sym_id,
                         lldb::LanguageType language,
                         lldb_private::LazyBool is_optimized)
    : ModuleChild(module_sp), FileSpec(pathname), UserID(cu_sym_id),
      m_user_data(user_data), m_language(language), m_flags(0),
      m_support_files(), m_line_table_ap(), m_variables(),
      m_is_optimized(is_optimized) {
  if (language != eLanguageTypeUnknown)
    m_flags.Set(flagsParsedLanguage);
  assert(module_sp);
}

CompileUnit::CompileUnit(const lldb::ModuleSP &module_sp, void *user_data,
                         const FileSpec &fspec, const lldb::user_id_t cu_sym_id,
                         lldb::LanguageType language,
                         lldb_private::LazyBool is_optimized)
    : ModuleChild(module_sp), FileSpec(fspec), UserID(cu_sym_id),
      m_user_data(user_data), m_language(language), m_flags(0),
      m_support_files(), m_line_table_ap(), m_variables(),
      m_is_optimized(is_optimized) {
  if (language != eLanguageTypeUnknown)
    m_flags.Set(flagsParsedLanguage);
  assert(module_sp);
}

CompileUnit::~CompileUnit() {}

void CompileUnit::CalculateSymbolContext(SymbolContext *sc) {
  sc->comp_unit = this;
  GetModule()->CalculateSymbolContext(sc);
}

ModuleSP CompileUnit::CalculateSymbolContextModule() { return GetModule(); }

CompileUnit *CompileUnit::CalculateSymbolContextCompileUnit() { return this; }

void CompileUnit::DumpSymbolContext(Stream *s) {
  GetModule()->DumpSymbolContext(s);
  s->Printf(", CompileUnit{0x%8.8" PRIx64 "}", GetID());
}

void CompileUnit::GetDescription(Stream *s,
                                 lldb::DescriptionLevel level) const {
  const char *language = Language::GetNameForLanguageType(m_language);
  *s << "id = " << (const UserID &)*this << ", file = \""
     << (const FileSpec &)*this << "\", language = \"" << language << '"';
}

void CompileUnit::ForeachFunction(
    llvm::function_ref<bool(const FunctionSP &)> lambda) const {
  std::vector<lldb::FunctionSP> sorted_functions;
  sorted_functions.reserve(m_functions_by_uid.size());
  for (auto &p : m_functions_by_uid)
    sorted_functions.push_back(p.second);
  llvm::sort(sorted_functions.begin(), sorted_functions.end(),
             [](const lldb::FunctionSP &a, const lldb::FunctionSP &b) {
               return a->GetID() < b->GetID();
             });

  for (auto &f : sorted_functions)
    if (lambda(f))
      return;
}

//----------------------------------------------------------------------
// Dump the current contents of this object. No functions that cause on demand
// parsing of functions, globals, statics are called, so this is a good
// function to call to get an idea of the current contents of the CompileUnit
// object.
//----------------------------------------------------------------------
void CompileUnit::Dump(Stream *s, bool show_context) const {
  const char *language = Language::GetNameForLanguageType(m_language);

  s->Printf("%p: ", static_cast<const void *>(this));
  s->Indent();
  *s << "CompileUnit" << static_cast<const UserID &>(*this) << ", language = \""
     << language << "\", file = '" << static_cast<const FileSpec &>(*this)
     << "'\n";

  //  m_types.Dump(s);

  if (m_variables.get()) {
    s->IndentMore();
    m_variables->Dump(s, show_context);
    s->IndentLess();
  }

  if (!m_functions_by_uid.empty()) {
    s->IndentMore();
    ForeachFunction([&s, show_context](const FunctionSP &f) {
      f->Dump(s, show_context);
      return false;
    });

    s->IndentLess();
    s->EOL();
  }
}

//----------------------------------------------------------------------
// Add a function to this compile unit
//----------------------------------------------------------------------
void CompileUnit::AddFunction(FunctionSP &funcSP) {
  m_functions_by_uid[funcSP->GetID()] = funcSP;
}

//----------------------------------------------------------------------
// Find functions using the Mangled::Tokens token list. This function currently
// implements an interactive approach designed to find all instances of certain
// functions. It isn't designed to the quickest way to lookup functions as it
// will need to iterate through all functions and see if they match, though it
// does provide a powerful and context sensitive way to search for all
// functions with a certain name, all functions in a namespace, or all
// functions of a template type. See Mangled::Tokens::Parse() comments for more
// information.
//
// The function prototype will need to change to return a list of results. It
// was originally used to help debug the Mangled class and the
// Mangled::Tokens::MatchesQuery() function and it currently will print out a
// list of matching results for the functions that are currently in this
// compile unit.
//
// A FindFunctions method should be called prior to this that takes
// a regular function name (const char * or ConstString as a parameter) before
// resorting to this slower but more complete function. The other FindFunctions
// method should be able to take advantage of any accelerator tables available
// in the debug information (which is parsed by the SymbolFile parser plug-ins
// and registered with each Module).
//----------------------------------------------------------------------
// void
// CompileUnit::FindFunctions(const Mangled::Tokens& tokens)
//{
//  if (!m_functions.empty())
//  {
//      Stream s(stdout);
//      std::vector<FunctionSP>::const_iterator pos;
//      std::vector<FunctionSP>::const_iterator end = m_functions.end();
//      for (pos = m_functions.begin(); pos != end; ++pos)
//      {
//          const ConstString& demangled = (*pos)->Mangled().Demangled();
//          if (demangled)
//          {
//              const Mangled::Tokens& func_tokens =
//              (*pos)->Mangled().GetTokens();
//              if (func_tokens.MatchesQuery (tokens))
//                  s << "demangled MATCH found: " << demangled << "\n";
//          }
//      }
//  }
//}

FunctionSP CompileUnit::FindFunctionByUID(lldb::user_id_t func_uid) {
  auto it = m_functions_by_uid.find(func_uid);
  if (it == m_functions_by_uid.end())
    return FunctionSP();
  return it->second;
}

lldb::LanguageType CompileUnit::GetLanguage() {
  if (m_language == eLanguageTypeUnknown) {
    if (m_flags.IsClear(flagsParsedLanguage)) {
      m_flags.Set(flagsParsedLanguage);
      SymbolVendor *symbol_vendor = GetModule()->GetSymbolVendor();
      if (symbol_vendor) {
        m_language = symbol_vendor->ParseLanguage(*this);
      }
    }
  }
  return m_language;
}

LineTable *CompileUnit::GetLineTable() {
  if (m_line_table_ap.get() == nullptr) {
    if (m_flags.IsClear(flagsParsedLineTable)) {
      m_flags.Set(flagsParsedLineTable);
      SymbolVendor *symbol_vendor = GetModule()->GetSymbolVendor();
      if (symbol_vendor)
        symbol_vendor->ParseLineTable(*this);
    }
  }
  return m_line_table_ap.get();
}

void CompileUnit::SetLineTable(LineTable *line_table) {
  if (line_table == nullptr)
    m_flags.Clear(flagsParsedLineTable);
  else
    m_flags.Set(flagsParsedLineTable);
  m_line_table_ap.reset(line_table);
}

DebugMacros *CompileUnit::GetDebugMacros() {
  if (m_debug_macros_sp.get() == nullptr) {
    if (m_flags.IsClear(flagsParsedDebugMacros)) {
      m_flags.Set(flagsParsedDebugMacros);
      SymbolVendor *symbol_vendor = GetModule()->GetSymbolVendor();
      if (symbol_vendor) {
        symbol_vendor->ParseDebugMacros(*this);
      }
    }
  }

  return m_debug_macros_sp.get();
}

void CompileUnit::SetDebugMacros(const DebugMacrosSP &debug_macros_sp) {
  if (debug_macros_sp.get() == nullptr)
    m_flags.Clear(flagsParsedDebugMacros);
  else
    m_flags.Set(flagsParsedDebugMacros);
  m_debug_macros_sp = debug_macros_sp;
}

VariableListSP CompileUnit::GetVariableList(bool can_create) {
  if (m_variables.get() == nullptr && can_create) {
    SymbolContext sc;
    CalculateSymbolContext(&sc);
    assert(sc.module_sp);
    sc.module_sp->GetSymbolVendor()->ParseVariablesForContext(sc);
  }

  return m_variables;
}

uint32_t CompileUnit::FindLineEntry(uint32_t start_idx, uint32_t line,
                                    const FileSpec *file_spec_ptr, bool exact,
                                    LineEntry *line_entry_ptr) {
  uint32_t file_idx = 0;

  if (file_spec_ptr) {
    file_idx = GetSupportFiles().FindFileIndex(1, *file_spec_ptr, true);
    if (file_idx == UINT32_MAX)
      return UINT32_MAX;
  } else {
    // All the line table entries actually point to the version of the Compile
    // Unit that is in the support files (the one at 0 was artificially added.)
    // So prefer the one further on in the support files if it exists...
    FileSpecList &support_files = GetSupportFiles();
    const bool full = true;
    file_idx = support_files.FindFileIndex(
        1, support_files.GetFileSpecAtIndex(0), full);
    if (file_idx == UINT32_MAX)
      file_idx = 0;
  }
  LineTable *line_table = GetLineTable();
  if (line_table)
    return line_table->FindLineEntryIndexByFileIndex(start_idx, file_idx, line,
                                                     exact, line_entry_ptr);
  return UINT32_MAX;
}

uint32_t CompileUnit::ResolveSymbolContext(const FileSpec &file_spec,
                                           uint32_t line, bool check_inlines,
                                           bool exact,
                                           SymbolContextItem resolve_scope,
                                           SymbolContextList &sc_list) {
  // First find all of the file indexes that match our "file_spec". If
  // "file_spec" has an empty directory, then only compare the basenames when
  // finding file indexes
  std::vector<uint32_t> file_indexes;
  const bool full_match = (bool)file_spec.GetDirectory();
  bool file_spec_matches_cu_file_spec =
      FileSpec::Equal(file_spec, *this, full_match);

  // If we are not looking for inlined functions and our file spec doesn't
  // match then we are done...
  if (!file_spec_matches_cu_file_spec && !check_inlines)
    return 0;

  uint32_t file_idx =
      GetSupportFiles().FindFileIndex(1, file_spec, true);
  while (file_idx != UINT32_MAX) {
    file_indexes.push_back(file_idx);
    file_idx = GetSupportFiles().FindFileIndex(file_idx + 1, file_spec, true);
  }

  const size_t num_file_indexes = file_indexes.size();
  if (num_file_indexes == 0)
    return 0;

  const uint32_t prev_size = sc_list.GetSize();

  SymbolContext sc(GetModule());
  sc.comp_unit = this;

  if (line != 0) {
    LineTable *line_table = sc.comp_unit->GetLineTable();

    if (line_table != nullptr) {
      uint32_t found_line;
      uint32_t line_idx;

      if (num_file_indexes == 1) {
        // We only have a single support file that matches, so use the line
        // table function that searches for a line entries that match a single
        // support file index
        LineEntry line_entry;
        line_idx = line_table->FindLineEntryIndexByFileIndex(
            0, file_indexes.front(), line, exact, &line_entry);

        // If "exact == true", then "found_line" will be the same as "line". If
        // "exact == false", the "found_line" will be the closest line entry
        // with a line number greater than "line" and we will use this for our
        // subsequent line exact matches below.
        found_line = line_entry.line;

        while (line_idx != UINT32_MAX) {
          // If they only asked for the line entry, then we're done, we can
          // just copy that over. But if they wanted more than just the line
          // number, fill it in.
          if (resolve_scope == eSymbolContextLineEntry) {
            sc.line_entry = line_entry;
          } else {
            line_entry.range.GetBaseAddress().CalculateSymbolContext(
                &sc, resolve_scope);
          }

          sc_list.Append(sc);
          line_idx = line_table->FindLineEntryIndexByFileIndex(
              line_idx + 1, file_indexes.front(), found_line, true,
              &line_entry);
        }
      } else {
        // We found multiple support files that match "file_spec" so use the
        // line table function that searches for a line entries that match a
        // multiple support file indexes.
        LineEntry line_entry;
        line_idx = line_table->FindLineEntryIndexByFileIndex(
            0, file_indexes, line, exact, &line_entry);

        // If "exact == true", then "found_line" will be the same as "line". If
        // "exact == false", the "found_line" will be the closest line entry
        // with a line number greater than "line" and we will use this for our
        // subsequent line exact matches below.
        found_line = line_entry.line;

        while (line_idx != UINT32_MAX) {
          if (resolve_scope == eSymbolContextLineEntry) {
            sc.line_entry = line_entry;
          } else {
            line_entry.range.GetBaseAddress().CalculateSymbolContext(
                &sc, resolve_scope);
          }

          sc_list.Append(sc);
          line_idx = line_table->FindLineEntryIndexByFileIndex(
              line_idx + 1, file_indexes, found_line, true, &line_entry);
        }
      }
    }
  } else if (file_spec_matches_cu_file_spec && !check_inlines) {
    // only append the context if we aren't looking for inline call sites by
    // file and line and if the file spec matches that of the compile unit
    sc_list.Append(sc);
  }
  return sc_list.GetSize() - prev_size;
}

bool CompileUnit::GetIsOptimized() {
  if (m_is_optimized == eLazyBoolCalculate) {
    m_is_optimized = eLazyBoolNo;
    if (SymbolVendor *symbol_vendor = GetModule()->GetSymbolVendor()) {
      if (symbol_vendor->ParseIsOptimized(*this))
        m_is_optimized = eLazyBoolYes;
    }
  }
  return m_is_optimized;
}

void CompileUnit::SetVariableList(VariableListSP &variables) {
  m_variables = variables;
}

const std::vector<ConstString> &CompileUnit::GetImportedModules() {
  if (m_imported_modules.empty() &&
      m_flags.IsClear(flagsParsedImportedModules)) {
    m_flags.Set(flagsParsedImportedModules);
    if (SymbolVendor *symbol_vendor = GetModule()->GetSymbolVendor()) {
      SymbolContext sc;
      CalculateSymbolContext(&sc);
      symbol_vendor->ParseImportedModules(sc, m_imported_modules);
    }
  }
  return m_imported_modules;
}

FileSpecList &CompileUnit::GetSupportFiles() {
  if (m_support_files.GetSize() == 0) {
    if (m_flags.IsClear(flagsParsedSupportFiles)) {
      m_flags.Set(flagsParsedSupportFiles);
      SymbolVendor *symbol_vendor = GetModule()->GetSymbolVendor();
      if (symbol_vendor) {
        symbol_vendor->ParseSupportFiles(*this, m_support_files);
      }
    }
  }
  return m_support_files;
}

void *CompileUnit::GetUserData() const { return m_user_data; }