summaryrefslogtreecommitdiff
path: root/src/core/core.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/core.cpp')
-rw-r--r--src/core/core.cpp515
1 files changed, 341 insertions, 174 deletions
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 2293669e5..75c259068 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -27,135 +27,299 @@ namespace Core {
27 27
28/*static*/ System System::s_instance; 28/*static*/ System System::s_instance;
29 29
30System::System() = default; 30namespace {
31FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
32 const std::string& path) {
33 // To account for split 00+01+etc files.
34 std::string dir_name;
35 std::string filename;
36 Common::SplitPath(path, &dir_name, &filename, nullptr);
37 if (filename == "00") {
38 const auto dir = vfs->OpenDirectory(dir_name, FileSys::Mode::Read);
39 std::vector<FileSys::VirtualFile> concat;
40 for (u8 i = 0; i < 0x10; ++i) {
41 auto next = dir->GetFile(fmt::format("{:02X}", i));
42 if (next != nullptr)
43 concat.push_back(std::move(next));
44 else {
45 next = dir->GetFile(fmt::format("{:02x}", i));
46 if (next != nullptr)
47 concat.push_back(std::move(next));
48 else
49 break;
50 }
51 }
31 52
32System::~System() = default; 53 if (concat.empty())
54 return nullptr;
55
56 return FileSys::ConcatenateFiles(concat, dir->GetName());
57 }
58
59 return vfs->OpenFile(path, FileSys::Mode::Read);
60}
33 61
34/// Runs a CPU core while the system is powered on 62/// Runs a CPU core while the system is powered on
35static void RunCpuCore(std::shared_ptr<Cpu> cpu_state) { 63void RunCpuCore(std::shared_ptr<Cpu> cpu_state) {
36 while (Core::System::GetInstance().IsPoweredOn()) { 64 while (Core::System::GetInstance().IsPoweredOn()) {
37 cpu_state->RunLoop(true); 65 cpu_state->RunLoop(true);
38 } 66 }
39} 67}
68} // Anonymous namespace
40 69
41Cpu& System::CurrentCpuCore() { 70struct System::Impl {
42 // If multicore is enabled, use host thread to figure out the current CPU core 71 Cpu& CurrentCpuCore() {
43 if (Settings::values.use_multi_core) { 72 if (Settings::values.use_multi_core) {
44 const auto& search = thread_to_cpu.find(std::this_thread::get_id()); 73 const auto& search = thread_to_cpu.find(std::this_thread::get_id());
45 ASSERT(search != thread_to_cpu.end()); 74 ASSERT(search != thread_to_cpu.end());
46 ASSERT(search->second); 75 ASSERT(search->second);
47 return *search->second; 76 return *search->second;
77 }
78
79 // Otherwise, use single-threaded mode active_core variable
80 return *cpu_cores[active_core];
48 } 81 }
49 82
50 // Otherwise, use single-threaded mode active_core variable 83 ResultStatus RunLoop(bool tight_loop) {
51 return *cpu_cores[active_core]; 84 status = ResultStatus::Success;
52}
53 85
54System::ResultStatus System::RunLoop(bool tight_loop) { 86 // Update thread_to_cpu in case Core 0 is run from a different host thread
55 status = ResultStatus::Success; 87 thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0];
56 88
57 // Update thread_to_cpu in case Core 0 is run from a different host thread 89 if (GDBStub::IsServerEnabled()) {
58 thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0]; 90 GDBStub::HandlePacket();
59 91
60 if (GDBStub::IsServerEnabled()) { 92 // If the loop is halted and we want to step, use a tiny (1) number of instructions to
61 GDBStub::HandlePacket(); 93 // execute. Otherwise, get out of the loop function.
94 if (GDBStub::GetCpuHaltFlag()) {
95 if (GDBStub::GetCpuStepFlag()) {
96 tight_loop = false;
97 } else {
98 return ResultStatus::Success;
99 }
100 }
101 }
62 102
63 // If the loop is halted and we want to step, use a tiny (1) number of instructions to 103 for (active_core = 0; active_core < NUM_CPU_CORES; ++active_core) {
64 // execute. Otherwise, get out of the loop function. 104 cpu_cores[active_core]->RunLoop(tight_loop);
65 if (GDBStub::GetCpuHaltFlag()) { 105 if (Settings::values.use_multi_core) {
66 if (GDBStub::GetCpuStepFlag()) { 106 // Cores 1-3 are run on other threads in this mode
67 tight_loop = false; 107 break;
68 } else {
69 return ResultStatus::Success;
70 } 108 }
71 } 109 }
110
111 if (GDBStub::IsServerEnabled()) {
112 GDBStub::SetCpuStepFlag(false);
113 }
114
115 return status;
72 } 116 }
73 117
74 for (active_core = 0; active_core < NUM_CPU_CORES; ++active_core) { 118 ResultStatus Init(Frontend::EmuWindow& emu_window) {
75 cpu_cores[active_core]->RunLoop(tight_loop); 119 LOG_DEBUG(HW_Memory, "initialized OK");
120
121 CoreTiming::Init();
122 kernel.Initialize();
123
124 // Create a default fs if one doesn't already exist.
125 if (virtual_filesystem == nullptr)
126 virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
127
128 current_process = Kernel::Process::Create(kernel, "main");
129
130 cpu_barrier = std::make_shared<CpuBarrier>();
131 cpu_exclusive_monitor = Cpu::MakeExclusiveMonitor(cpu_cores.size());
132 for (size_t index = 0; index < cpu_cores.size(); ++index) {
133 cpu_cores[index] = std::make_shared<Cpu>(cpu_exclusive_monitor, cpu_barrier, index);
134 }
135
136 telemetry_session = std::make_unique<Core::TelemetrySession>();
137 service_manager = std::make_shared<Service::SM::ServiceManager>();
138
139 Service::Init(service_manager, virtual_filesystem);
140 GDBStub::Init();
141
142 renderer = VideoCore::CreateRenderer(emu_window);
143 if (!renderer->Init()) {
144 return ResultStatus::ErrorVideoCore;
145 }
146
147 gpu_core = std::make_unique<Tegra::GPU>(renderer->Rasterizer());
148
149 // Create threads for CPU cores 1-3, and build thread_to_cpu map
150 // CPU core 0 is run on the main thread
151 thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0];
76 if (Settings::values.use_multi_core) { 152 if (Settings::values.use_multi_core) {
77 // Cores 1-3 are run on other threads in this mode 153 for (size_t index = 0; index < cpu_core_threads.size(); ++index) {
78 break; 154 cpu_core_threads[index] =
155 std::make_unique<std::thread>(RunCpuCore, cpu_cores[index + 1]);
156 thread_to_cpu[cpu_core_threads[index]->get_id()] = cpu_cores[index + 1];
157 }
79 } 158 }
80 }
81 159
82 if (GDBStub::IsServerEnabled()) { 160 LOG_DEBUG(Core, "Initialized OK");
83 GDBStub::SetCpuStepFlag(false); 161
162 // Reset counters and set time origin to current frame
163 GetAndResetPerfStats();
164 perf_stats.BeginSystemFrame();
165
166 return ResultStatus::Success;
84 } 167 }
85 168
86 return status; 169 ResultStatus Load(Frontend::EmuWindow& emu_window, const std::string& filepath) {
87} 170 app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath));
88 171
89System::ResultStatus System::SingleStep() { 172 if (!app_loader) {
90 return RunLoop(false); 173 LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
91} 174 return ResultStatus::ErrorGetLoader;
175 }
176 std::pair<boost::optional<u32>, Loader::ResultStatus> system_mode =
177 app_loader->LoadKernelSystemMode();
92 178
93static FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, 179 if (system_mode.second != Loader::ResultStatus::Success) {
94 const std::string& path) { 180 LOG_CRITICAL(Core, "Failed to determine system mode (Error {})!",
95 // To account for split 00+01+etc files. 181 static_cast<int>(system_mode.second));
96 std::string dir_name; 182
97 std::string filename; 183 return ResultStatus::ErrorSystemMode;
98 Common::SplitPath(path, &dir_name, &filename, nullptr);
99 if (filename == "00") {
100 const auto dir = vfs->OpenDirectory(dir_name, FileSys::Mode::Read);
101 std::vector<FileSys::VirtualFile> concat;
102 for (u8 i = 0; i < 0x10; ++i) {
103 auto next = dir->GetFile(fmt::format("{:02X}", i));
104 if (next != nullptr)
105 concat.push_back(std::move(next));
106 else {
107 next = dir->GetFile(fmt::format("{:02x}", i));
108 if (next != nullptr)
109 concat.push_back(std::move(next));
110 else
111 break;
112 }
113 } 184 }
114 185
115 if (concat.empty()) 186 ResultStatus init_result{Init(emu_window)};
116 return nullptr; 187 if (init_result != ResultStatus::Success) {
188 LOG_CRITICAL(Core, "Failed to initialize system (Error {})!",
189 static_cast<int>(init_result));
190 Shutdown();
191 return init_result;
192 }
117 193
118 return FileSys::ConcatenateFiles(concat, dir->GetName()); 194 const Loader::ResultStatus load_result{app_loader->Load(current_process)};
195 if (load_result != Loader::ResultStatus::Success) {
196 LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<int>(load_result));
197 Shutdown();
198
199 return static_cast<ResultStatus>(static_cast<u32>(ResultStatus::ErrorLoader) +
200 static_cast<u32>(load_result));
201 }
202 status = ResultStatus::Success;
203 return status;
119 } 204 }
120 205
121 return vfs->OpenFile(path, FileSys::Mode::Read); 206 void Shutdown() {
122} 207 // Log last frame performance stats
208 auto perf_results = GetAndResetPerfStats();
209 Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_EmulationSpeed",
210 perf_results.emulation_speed * 100.0);
211 Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_Framerate",
212 perf_results.game_fps);
213 Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime",
214 perf_results.frametime * 1000.0);
215
216 // Shutdown emulation session
217 renderer.reset();
218 GDBStub::Shutdown();
219 Service::Shutdown();
220 service_manager.reset();
221 telemetry_session.reset();
222 gpu_core.reset();
223
224 // Close all CPU/threading state
225 cpu_barrier->NotifyEnd();
226 if (Settings::values.use_multi_core) {
227 for (auto& thread : cpu_core_threads) {
228 thread->join();
229 thread.reset();
230 }
231 }
232 thread_to_cpu.clear();
233 for (auto& cpu_core : cpu_cores) {
234 cpu_core.reset();
235 }
236 cpu_barrier.reset();
123 237
124System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath) { 238 // Shutdown kernel and core timing
125 app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath)); 239 kernel.Shutdown();
240 CoreTiming::Shutdown();
241
242 // Close app loader
243 app_loader.reset();
126 244
127 if (!app_loader) { 245 LOG_DEBUG(Core, "Shutdown OK");
128 LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
129 return ResultStatus::ErrorGetLoader;
130 } 246 }
131 std::pair<boost::optional<u32>, Loader::ResultStatus> system_mode =
132 app_loader->LoadKernelSystemMode();
133 247
134 if (system_mode.second != Loader::ResultStatus::Success) { 248 Loader::ResultStatus GetGameName(std::string& out) const {
135 LOG_CRITICAL(Core, "Failed to determine system mode (Error {})!", 249 if (app_loader == nullptr)
136 static_cast<int>(system_mode.second)); 250 return Loader::ResultStatus::ErrorNotInitialized;
251 return app_loader->ReadTitle(out);
252 }
137 253
138 return ResultStatus::ErrorSystemMode; 254 void SetStatus(ResultStatus new_status, const char* details = nullptr) {
255 status = new_status;
256 if (details) {
257 status_details = details;
258 }
139 } 259 }
140 260
141 ResultStatus init_result{Init(emu_window)}; 261 PerfStats::Results GetAndResetPerfStats() {
142 if (init_result != ResultStatus::Success) { 262 return perf_stats.GetAndResetStats(CoreTiming::GetGlobalTimeUs());
143 LOG_CRITICAL(Core, "Failed to initialize system (Error {})!",
144 static_cast<int>(init_result));
145 System::Shutdown();
146 return init_result;
147 } 263 }
148 264
149 const Loader::ResultStatus load_result{app_loader->Load(current_process)}; 265 Kernel::KernelCore kernel;
150 if (load_result != Loader::ResultStatus::Success) { 266 /// RealVfsFilesystem instance
151 LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<int>(load_result)); 267 FileSys::VirtualFilesystem virtual_filesystem;
152 System::Shutdown(); 268 /// AppLoader used to load the current executing application
269 std::unique_ptr<Loader::AppLoader> app_loader;
270 std::unique_ptr<VideoCore::RendererBase> renderer;
271 std::unique_ptr<Tegra::GPU> gpu_core;
272 std::shared_ptr<Tegra::DebugContext> debug_context;
273 Kernel::SharedPtr<Kernel::Process> current_process;
274 std::shared_ptr<ExclusiveMonitor> cpu_exclusive_monitor;
275 std::shared_ptr<CpuBarrier> cpu_barrier;
276 std::array<std::shared_ptr<Cpu>, NUM_CPU_CORES> cpu_cores;
277 std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> cpu_core_threads;
278 size_t active_core{}; ///< Active core, only used in single thread mode
279
280 /// Service manager
281 std::shared_ptr<Service::SM::ServiceManager> service_manager;
282
283 /// Telemetry session for this emulation session
284 std::unique_ptr<Core::TelemetrySession> telemetry_session;
285
286 ResultStatus status = ResultStatus::Success;
287 std::string status_details = "";
288
289 /// Map of guest threads to CPU cores
290 std::map<std::thread::id, std::shared_ptr<Cpu>> thread_to_cpu;
291
292 Core::PerfStats perf_stats;
293 Core::FrameLimiter frame_limiter;
294};
295
296System::System() : impl{std::make_unique<Impl>()} {}
297System::~System() = default;
298
299Cpu& System::CurrentCpuCore() {
300 return impl->CurrentCpuCore();
301}
302
303System::ResultStatus System::RunLoop(bool tight_loop) {
304 return impl->RunLoop(tight_loop);
305}
306
307System::ResultStatus System::SingleStep() {
308 return RunLoop(false);
309}
153 310
154 return static_cast<ResultStatus>(static_cast<u32>(ResultStatus::ErrorLoader) + 311void System::InvalidateCpuInstructionCaches() {
155 static_cast<u32>(load_result)); 312 for (auto& cpu : impl->cpu_cores) {
313 cpu->ArmInterface().ClearInstructionCache();
156 } 314 }
157 status = ResultStatus::Success; 315}
158 return status; 316
317System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath) {
318 return impl->Load(emu_window, filepath);
319}
320
321bool System::IsPoweredOn() const {
322 return impl->cpu_barrier && impl->cpu_barrier->IsAlive();
159} 323}
160 324
161void System::PrepareReschedule() { 325void System::PrepareReschedule() {
@@ -163,131 +327,134 @@ void System::PrepareReschedule() {
163} 327}
164 328
165PerfStats::Results System::GetAndResetPerfStats() { 329PerfStats::Results System::GetAndResetPerfStats() {
166 return perf_stats.GetAndResetStats(CoreTiming::GetGlobalTimeUs()); 330 return impl->GetAndResetPerfStats();
167} 331}
168 332
169const std::shared_ptr<Kernel::Scheduler>& System::Scheduler(size_t core_index) { 333Core::TelemetrySession& System::TelemetrySession() const {
170 ASSERT(core_index < NUM_CPU_CORES); 334 return *impl->telemetry_session;
171 return cpu_cores[core_index]->Scheduler();
172} 335}
173 336
174Kernel::KernelCore& System::Kernel() { 337ARM_Interface& System::CurrentArmInterface() {
175 return kernel; 338 return CurrentCpuCore().ArmInterface();
176} 339}
177 340
178const Kernel::KernelCore& System::Kernel() const { 341size_t System::CurrentCoreIndex() {
179 return kernel; 342 return CurrentCpuCore().CoreIndex();
343}
344
345Kernel::Scheduler& System::CurrentScheduler() {
346 return *CurrentCpuCore().Scheduler();
347}
348
349const std::shared_ptr<Kernel::Scheduler>& System::Scheduler(size_t core_index) {
350 ASSERT(core_index < NUM_CPU_CORES);
351 return impl->cpu_cores[core_index]->Scheduler();
352}
353
354Kernel::SharedPtr<Kernel::Process>& System::CurrentProcess() {
355 return impl->current_process;
180} 356}
181 357
182ARM_Interface& System::ArmInterface(size_t core_index) { 358ARM_Interface& System::ArmInterface(size_t core_index) {
183 ASSERT(core_index < NUM_CPU_CORES); 359 ASSERT(core_index < NUM_CPU_CORES);
184 return cpu_cores[core_index]->ArmInterface(); 360 return impl->cpu_cores[core_index]->ArmInterface();
185} 361}
186 362
187Cpu& System::CpuCore(size_t core_index) { 363Cpu& System::CpuCore(size_t core_index) {
188 ASSERT(core_index < NUM_CPU_CORES); 364 ASSERT(core_index < NUM_CPU_CORES);
189 return *cpu_cores[core_index]; 365 return *impl->cpu_cores[core_index];
190} 366}
191 367
192System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) { 368ExclusiveMonitor& System::Monitor() {
193 LOG_DEBUG(HW_Memory, "initialized OK"); 369 return *impl->cpu_exclusive_monitor;
370}
194 371
195 CoreTiming::Init(); 372Tegra::GPU& System::GPU() {
196 kernel.Initialize(); 373 return *impl->gpu_core;
374}
197 375
198 // Create a default fs if one doesn't already exist. 376const Tegra::GPU& System::GPU() const {
199 if (virtual_filesystem == nullptr) 377 return *impl->gpu_core;
200 virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>(); 378}
201 379
202 current_process = Kernel::Process::Create(kernel, "main"); 380VideoCore::RendererBase& System::Renderer() {
381 return *impl->renderer;
382}
203 383
204 cpu_barrier = std::make_shared<CpuBarrier>(); 384const VideoCore::RendererBase& System::Renderer() const {
205 cpu_exclusive_monitor = Cpu::MakeExclusiveMonitor(cpu_cores.size()); 385 return *impl->renderer;
206 for (size_t index = 0; index < cpu_cores.size(); ++index) { 386}
207 cpu_cores[index] = std::make_shared<Cpu>(cpu_exclusive_monitor, cpu_barrier, index);
208 }
209 387
210 telemetry_session = std::make_unique<Core::TelemetrySession>(); 388Kernel::KernelCore& System::Kernel() {
211 service_manager = std::make_shared<Service::SM::ServiceManager>(); 389 return impl->kernel;
390}
212 391
213 Service::Init(service_manager, virtual_filesystem); 392const Kernel::KernelCore& System::Kernel() const {
214 GDBStub::Init(); 393 return impl->kernel;
394}
215 395
216 renderer = VideoCore::CreateRenderer(emu_window); 396Core::PerfStats& System::GetPerfStats() {
217 if (!renderer->Init()) { 397 return impl->perf_stats;
218 return ResultStatus::ErrorVideoCore; 398}
219 }
220 399
221 gpu_core = std::make_unique<Tegra::GPU>(renderer->Rasterizer()); 400const Core::PerfStats& System::GetPerfStats() const {
401 return impl->perf_stats;
402}
222 403
223 // Create threads for CPU cores 1-3, and build thread_to_cpu map 404Core::FrameLimiter& System::FrameLimiter() {
224 // CPU core 0 is run on the main thread 405 return impl->frame_limiter;
225 thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0]; 406}
226 if (Settings::values.use_multi_core) {
227 for (size_t index = 0; index < cpu_core_threads.size(); ++index) {
228 cpu_core_threads[index] =
229 std::make_unique<std::thread>(RunCpuCore, cpu_cores[index + 1]);
230 thread_to_cpu[cpu_core_threads[index]->get_id()] = cpu_cores[index + 1];
231 }
232 }
233 407
234 LOG_DEBUG(Core, "Initialized OK"); 408const Core::FrameLimiter& System::FrameLimiter() const {
409 return impl->frame_limiter;
410}
235 411
236 // Reset counters and set time origin to current frame 412Loader::ResultStatus System::GetGameName(std::string& out) const {
237 GetAndResetPerfStats(); 413 return impl->GetGameName(out);
238 perf_stats.BeginSystemFrame(); 414}
239 415
240 return ResultStatus::Success; 416void System::SetStatus(ResultStatus new_status, const char* details) {
417 impl->SetStatus(new_status, details);
241} 418}
242 419
243void System::Shutdown() { 420const std::string& System::GetStatusDetails() const {
244 // Log last frame performance stats 421 return impl->status_details;
245 auto perf_results = GetAndResetPerfStats(); 422}
246 Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_EmulationSpeed",
247 perf_results.emulation_speed * 100.0);
248 Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_Framerate",
249 perf_results.game_fps);
250 Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime",
251 perf_results.frametime * 1000.0);
252
253 // Shutdown emulation session
254 renderer.reset();
255 GDBStub::Shutdown();
256 Service::Shutdown();
257 service_manager.reset();
258 telemetry_session.reset();
259 gpu_core.reset();
260
261 // Close all CPU/threading state
262 cpu_barrier->NotifyEnd();
263 if (Settings::values.use_multi_core) {
264 for (auto& thread : cpu_core_threads) {
265 thread->join();
266 thread.reset();
267 }
268 }
269 thread_to_cpu.clear();
270 for (auto& cpu_core : cpu_cores) {
271 cpu_core.reset();
272 }
273 cpu_barrier.reset();
274 423
275 // Shutdown kernel and core timing 424Loader::AppLoader& System::GetAppLoader() const {
276 kernel.Shutdown(); 425 return *impl->app_loader;
277 CoreTiming::Shutdown(); 426}
278 427
279 // Close app loader 428void System::SetGPUDebugContext(std::shared_ptr<Tegra::DebugContext> context) {
280 app_loader.reset(); 429 impl->debug_context = std::move(context);
430}
281 431
282 LOG_DEBUG(Core, "Shutdown OK"); 432std::shared_ptr<Tegra::DebugContext> System::GetGPUDebugContext() const {
433 return impl->debug_context;
434}
435
436void System::SetFilesystem(FileSys::VirtualFilesystem vfs) {
437 impl->virtual_filesystem = std::move(vfs);
438}
439
440FileSys::VirtualFilesystem System::GetFilesystem() const {
441 return impl->virtual_filesystem;
442}
443
444System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) {
445 return impl->Init(emu_window);
446}
447
448void System::Shutdown() {
449 impl->Shutdown();
283} 450}
284 451
285Service::SM::ServiceManager& System::ServiceManager() { 452Service::SM::ServiceManager& System::ServiceManager() {
286 return *service_manager; 453 return *impl->service_manager;
287} 454}
288 455
289const Service::SM::ServiceManager& System::ServiceManager() const { 456const Service::SM::ServiceManager& System::ServiceManager() const {
290 return *service_manager; 457 return *impl->service_manager;
291} 458}
292 459
293} // namespace Core 460} // namespace Core