aboutsummaryrefslogtreecommitdiff
path: root/lldb/source/Symbol/SaveCoreOptions.cpp
blob: 6d762a66181cf6704e1d42f6fae0ff417c6709f1 (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
//===-- SaveCoreOptions.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/SaveCoreOptions.h"
#include "lldb/Core/PluginManager.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/Thread.h"

using namespace lldb;
using namespace lldb_private;

Status SaveCoreOptions::SetPluginName(const char *name) {
  Status error;
  if (!name || !name[0]) {
    m_plugin_name = std::nullopt;
    return error;
  }

  std::vector<llvm::StringRef> plugin_names =
      PluginManager::GetSaveCorePluginNames();
  if (!llvm::is_contained(plugin_names, name)) {
    StreamString stream;
    stream.Printf("plugin name '%s' is not a valid ObjectFile plugin name.",
                  name);

    if (!plugin_names.empty()) {
      stream.PutCString(" Valid names are: ");
      std::string plugin_names_str = llvm::join(plugin_names, ", ");
      stream.PutCString(plugin_names_str);
      stream.PutChar('.');
    }
    return Status(stream.GetString().str());
  }

  m_plugin_name = name;
  return error;
}

void SaveCoreOptions::SetStyle(lldb::SaveCoreStyle style) { m_style = style; }

void SaveCoreOptions::SetOutputFile(FileSpec file) { m_file = file; }

std::optional<std::string> SaveCoreOptions::GetPluginName() const {
  return m_plugin_name;
}

lldb::SaveCoreStyle SaveCoreOptions::GetStyle() const {
  return m_style.value_or(lldb::eSaveCoreUnspecified);
}

const std::optional<lldb_private::FileSpec>
SaveCoreOptions::GetOutputFile() const {
  return m_file;
}

Status SaveCoreOptions::SetProcess(lldb::ProcessSP process_sp) {
  Status error;
  if (!process_sp) {
    ClearProcessSpecificData();
    m_process_sp.reset();
    return error;
  }

  if (!process_sp->IsValid()) {
    error = Status::FromErrorString("Cannot assign an invalid process.");
    return error;
  }

  // Don't clear any process specific data if the process is the same.
  if (m_process_sp == process_sp)
    return error;

  ClearProcessSpecificData();
  m_process_sp = process_sp;
  return error;
}

Status SaveCoreOptions::AddThread(lldb::ThreadSP thread_sp) {
  Status error;
  if (!thread_sp) {
    error = Status::FromErrorString("invalid thread");
    return error;
  }

  if (m_process_sp) {
    if (m_process_sp != thread_sp->GetProcess()) {
      error = Status::FromErrorString(
          "Cannot add a thread from a different process.");
      return error;
    }
  } else {
    m_process_sp = thread_sp->GetProcess();
  }

  m_threads_to_save.insert(thread_sp->GetID());
  return error;
}

bool SaveCoreOptions::RemoveThread(lldb::ThreadSP thread_sp) {
  return thread_sp && m_threads_to_save.erase(thread_sp->GetID()) > 0;
}

bool SaveCoreOptions::ShouldThreadBeSaved(lldb::tid_t tid) const {
  // If the user specified no threads to save, then we save all threads.
  if (m_threads_to_save.empty())
    return true;
  return m_threads_to_save.count(tid) > 0;
}

bool SaveCoreOptions::HasSpecifiedThreads() const {
  return !m_threads_to_save.empty();
}

void SaveCoreOptions::AddMemoryRegionToSave(
    const lldb_private::MemoryRegionInfo &region) {
  m_regions_to_save.Insert(region.GetRange(), /*combine=*/true);
}

const MemoryRanges &SaveCoreOptions::GetCoreFileMemoryRanges() const {
  return m_regions_to_save;
}
Status SaveCoreOptions::EnsureValidConfiguration() const {
  Status error;
  std::string error_str;
  if (!m_threads_to_save.empty() && GetStyle() == lldb::eSaveCoreFull)
    error_str += "Cannot save a full core with a subset of threads\n";

  if (!m_process_sp)
    error_str += "Need to assign a valid process\n";

  if (!error_str.empty())
    error = Status(error_str);

  return error;
}

lldb_private::ThreadCollection::collection
SaveCoreOptions::GetThreadsToSave() const {
  lldb_private::ThreadCollection::collection thread_collection;
  // In cases where no process is set, such as when no threads are specified.
  if (!m_process_sp)
    return thread_collection;

  ThreadList &thread_list = m_process_sp->GetThreadList();
  for (const auto &tid : m_threads_to_save)
    thread_collection.push_back(thread_list.FindThreadByID(tid));

  return thread_collection;
}

llvm::Expected<lldb_private::CoreFileMemoryRanges>
SaveCoreOptions::GetMemoryRegionsToSave() {
  Status error;
  if (!m_process_sp)
    return Status::FromErrorString("Requires a process to be set.").takeError();

  error = EnsureValidConfiguration();
  if (error.Fail())
    return error.takeError();

  CoreFileMemoryRanges ranges;
  error = m_process_sp->CalculateCoreFileSaveRanges(*this, ranges);
  if (error.Fail())
    return error.takeError();

  return ranges;
}

llvm::Expected<uint64_t> SaveCoreOptions::GetCurrentSizeInBytes() {
  Status error;
  if (!m_process_sp)
    return Status::FromErrorString("Requires a process to be set.").takeError();

  error = EnsureValidConfiguration();
  if (error.Fail())
    return error.takeError();

  CoreFileMemoryRanges ranges;
  error = m_process_sp->CalculateCoreFileSaveRanges(*this, ranges);
  if (error.Fail())
    return error.takeError();

  llvm::Expected<lldb_private::CoreFileMemoryRanges> core_file_ranges_maybe =
      GetMemoryRegionsToSave();
  if (!core_file_ranges_maybe)
    return core_file_ranges_maybe.takeError();
  const lldb_private::CoreFileMemoryRanges &core_file_ranges =
      *core_file_ranges_maybe;
  uint64_t total_in_bytes = 0;
  for (const auto &core_range : core_file_ranges)
    total_in_bytes += core_range.data.range.size();

  return total_in_bytes;
}

void SaveCoreOptions::ClearProcessSpecificData() {
  // Deliberately not following the formatter style here to indicate that
  // this method will be expanded in the future.
  m_threads_to_save.clear();
}

void SaveCoreOptions::Clear() {
  m_file = std::nullopt;
  m_plugin_name = std::nullopt;
  m_style = std::nullopt;
  m_threads_to_save.clear();
  m_process_sp.reset();
  m_regions_to_save.Clear();
}