1//===-- SaveCoreOptions.cpp -------------------------------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "lldb/Symbol/SaveCoreOptions.h"
10#include "lldb/Core/PluginManager.h"
11#include "lldb/Target/Process.h"
12#include "lldb/Target/Thread.h"
13
14using namespace lldb;
15using namespace lldb_private;
16
17Status SaveCoreOptions::SetPluginName(const char *name) {
18 Status error;
19 if (!name || !name[0]) {
20 m_plugin_name = std::nullopt;
21 return error;
22 }
23
24 std::vector<llvm::StringRef> plugin_names =
25 PluginManager::GetSaveCorePluginNames();
26 if (!llvm::is_contained(Range&: plugin_names, Element: name)) {
27 StreamString stream;
28 stream.Printf(format: "plugin name '%s' is not a valid ObjectFile plugin name.",
29 name);
30
31 if (!plugin_names.empty()) {
32 stream.PutCString(cstr: " Valid names are: ");
33 std::string plugin_names_str = llvm::join(R&: plugin_names, Separator: ", ");
34 stream.PutCString(cstr: plugin_names_str);
35 stream.PutChar(ch: '.');
36 }
37 return Status(stream.GetString().str());
38 }
39
40 m_plugin_name = name;
41 return error;
42}
43
44void SaveCoreOptions::SetStyle(lldb::SaveCoreStyle style) { m_style = style; }
45
46void SaveCoreOptions::SetOutputFile(FileSpec file) { m_file = file; }
47
48std::optional<std::string> SaveCoreOptions::GetPluginName() const {
49 return m_plugin_name;
50}
51
52lldb::SaveCoreStyle SaveCoreOptions::GetStyle() const {
53 return m_style.value_or(u: lldb::eSaveCoreUnspecified);
54}
55
56const std::optional<lldb_private::FileSpec>
57SaveCoreOptions::GetOutputFile() const {
58 return m_file;
59}
60
61Status SaveCoreOptions::SetProcess(lldb::ProcessSP process_sp) {
62 Status error;
63 if (!process_sp) {
64 ClearProcessSpecificData();
65 m_process_sp.reset();
66 return error;
67 }
68
69 if (!process_sp->IsValid()) {
70 error = Status::FromErrorString(str: "Cannot assign an invalid process.");
71 return error;
72 }
73
74 // Don't clear any process specific data if the process is the same.
75 if (m_process_sp == process_sp)
76 return error;
77
78 ClearProcessSpecificData();
79 m_process_sp = process_sp;
80 return error;
81}
82
83Status SaveCoreOptions::AddThread(lldb::ThreadSP thread_sp) {
84 Status error;
85 if (!thread_sp) {
86 error = Status::FromErrorString(str: "invalid thread");
87 return error;
88 }
89
90 if (m_process_sp) {
91 if (m_process_sp != thread_sp->GetProcess()) {
92 error = Status::FromErrorString(
93 str: "Cannot add a thread from a different process.");
94 return error;
95 }
96 } else {
97 m_process_sp = thread_sp->GetProcess();
98 }
99
100 m_threads_to_save.insert(x: thread_sp->GetID());
101 return error;
102}
103
104bool SaveCoreOptions::RemoveThread(lldb::ThreadSP thread_sp) {
105 return thread_sp && m_threads_to_save.erase(x: thread_sp->GetID()) > 0;
106}
107
108bool SaveCoreOptions::ShouldThreadBeSaved(lldb::tid_t tid) const {
109 // If the user specified no threads to save, then we save all threads.
110 if (m_threads_to_save.empty())
111 return true;
112 return m_threads_to_save.count(x: tid) > 0;
113}
114
115bool SaveCoreOptions::HasSpecifiedThreads() const {
116 return !m_threads_to_save.empty();
117}
118
119void SaveCoreOptions::AddMemoryRegionToSave(
120 const lldb_private::MemoryRegionInfo &region) {
121 m_regions_to_save.Insert(entry: region.GetRange(), /*combine=*/true);
122}
123
124const MemoryRanges &SaveCoreOptions::GetCoreFileMemoryRanges() const {
125 return m_regions_to_save;
126}
127Status
128SaveCoreOptions::EnsureValidConfiguration(lldb::ProcessSP process_sp) const {
129 Status error;
130 std::string error_str;
131 if (!m_threads_to_save.empty() && GetStyle() == lldb::eSaveCoreFull)
132 error_str += "Cannot save a full core with a subset of threads\n";
133
134 if (m_process_sp && m_process_sp != process_sp)
135 error_str += "Cannot save core for process using supplied core options. "
136 "Options were constructed targeting a different process. \n";
137
138 if (!error_str.empty())
139 error = Status(error_str);
140
141 return error;
142}
143
144lldb_private::ThreadCollection::collection
145SaveCoreOptions::GetThreadsToSave() const {
146 lldb_private::ThreadCollection::collection thread_collection;
147 // In cases where no process is set, such as when no threads are specified.
148 if (!m_process_sp)
149 return thread_collection;
150
151 ThreadList &thread_list = m_process_sp->GetThreadList();
152 for (const auto &tid : m_threads_to_save)
153 thread_collection.push_back(x: thread_list.FindThreadByID(tid));
154
155 return thread_collection;
156}
157
158llvm::Expected<uint64_t> SaveCoreOptions::GetCurrentSizeInBytes() {
159 Status error;
160 if (!m_process_sp)
161 return Status::FromErrorString(str: "Requires a process to be set.").takeError();
162
163 error = EnsureValidConfiguration(process_sp: m_process_sp);
164 if (error.Fail())
165 return error.takeError();
166
167 CoreFileMemoryRanges ranges;
168 error = m_process_sp->CalculateCoreFileSaveRanges(core_options: *this, ranges);
169 if (error.Fail())
170 return error.takeError();
171
172 uint64_t total_in_bytes = 0;
173 for (auto &core_range : ranges)
174 total_in_bytes += core_range.data.range.size();
175
176 return total_in_bytes;
177}
178
179void SaveCoreOptions::ClearProcessSpecificData() {
180 // Deliberately not following the formatter style here to indicate that
181 // this method will be expanded in the future.
182 m_threads_to_save.clear();
183}
184
185void SaveCoreOptions::Clear() {
186 m_file = std::nullopt;
187 m_plugin_name = std::nullopt;
188 m_style = std::nullopt;
189 m_threads_to_save.clear();
190 m_process_sp.reset();
191 m_regions_to_save.Clear();
192}
193

source code of lldb/source/Symbol/SaveCoreOptions.cpp