xref: /aosp_15_r20/system/unwinding/libunwindstack/benchmarks/local_unwind_benchmarks.cpp (revision eb293b8f56ee8303637c5595cfcdeef8039e85c6)
1*eb293b8fSAndroid Build Coastguard Worker /*
2*eb293b8fSAndroid Build Coastguard Worker  * Copyright (C) 2018 The Android Open Source Project
3*eb293b8fSAndroid Build Coastguard Worker  *
4*eb293b8fSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*eb293b8fSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*eb293b8fSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*eb293b8fSAndroid Build Coastguard Worker  *
8*eb293b8fSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*eb293b8fSAndroid Build Coastguard Worker  *
10*eb293b8fSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*eb293b8fSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*eb293b8fSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*eb293b8fSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*eb293b8fSAndroid Build Coastguard Worker  * limitations under the License.
15*eb293b8fSAndroid Build Coastguard Worker  */
16*eb293b8fSAndroid Build Coastguard Worker 
17*eb293b8fSAndroid Build Coastguard Worker #include <stdint.h>
18*eb293b8fSAndroid Build Coastguard Worker 
19*eb293b8fSAndroid Build Coastguard Worker #include <memory>
20*eb293b8fSAndroid Build Coastguard Worker 
21*eb293b8fSAndroid Build Coastguard Worker #include <benchmark/benchmark.h>
22*eb293b8fSAndroid Build Coastguard Worker 
23*eb293b8fSAndroid Build Coastguard Worker #include <android-base/strings.h>
24*eb293b8fSAndroid Build Coastguard Worker 
25*eb293b8fSAndroid Build Coastguard Worker #include <unwindstack/AndroidUnwinder.h>
26*eb293b8fSAndroid Build Coastguard Worker #include <unwindstack/Maps.h>
27*eb293b8fSAndroid Build Coastguard Worker #include <unwindstack/Memory.h>
28*eb293b8fSAndroid Build Coastguard Worker #include <unwindstack/Regs.h>
29*eb293b8fSAndroid Build Coastguard Worker #include <unwindstack/RegsGetLocal.h>
30*eb293b8fSAndroid Build Coastguard Worker #include <unwindstack/Unwinder.h>
31*eb293b8fSAndroid Build Coastguard Worker #include "MemoryLocalUnsafe.h"
32*eb293b8fSAndroid Build Coastguard Worker 
33*eb293b8fSAndroid Build Coastguard Worker constexpr size_t kMaxFrames = 32;
34*eb293b8fSAndroid Build Coastguard Worker 
35*eb293b8fSAndroid Build Coastguard Worker struct UnwindData {
36*eb293b8fSAndroid Build Coastguard Worker   std::shared_ptr<unwindstack::Memory>& process_memory;
37*eb293b8fSAndroid Build Coastguard Worker   unwindstack::Maps* maps;
38*eb293b8fSAndroid Build Coastguard Worker   bool resolve_names;
39*eb293b8fSAndroid Build Coastguard Worker };
40*eb293b8fSAndroid Build Coastguard Worker 
LocalCall5(size_t (* func)(void *),void * data)41*eb293b8fSAndroid Build Coastguard Worker size_t LocalCall5(size_t (*func)(void*), void* data) {
42*eb293b8fSAndroid Build Coastguard Worker   return func(data);
43*eb293b8fSAndroid Build Coastguard Worker }
44*eb293b8fSAndroid Build Coastguard Worker 
LocalCall4(size_t (* func)(void *),void * data)45*eb293b8fSAndroid Build Coastguard Worker size_t LocalCall4(size_t (*func)(void*), void* data) {
46*eb293b8fSAndroid Build Coastguard Worker   return LocalCall5(func, data);
47*eb293b8fSAndroid Build Coastguard Worker }
48*eb293b8fSAndroid Build Coastguard Worker 
LocalCall3(size_t (* func)(void *),void * data)49*eb293b8fSAndroid Build Coastguard Worker size_t LocalCall3(size_t (*func)(void*), void* data) {
50*eb293b8fSAndroid Build Coastguard Worker   return LocalCall4(func, data);
51*eb293b8fSAndroid Build Coastguard Worker }
52*eb293b8fSAndroid Build Coastguard Worker 
LocalCall2(size_t (* func)(void *),void * data)53*eb293b8fSAndroid Build Coastguard Worker size_t LocalCall2(size_t (*func)(void*), void* data) {
54*eb293b8fSAndroid Build Coastguard Worker   return LocalCall3(func, data);
55*eb293b8fSAndroid Build Coastguard Worker }
56*eb293b8fSAndroid Build Coastguard Worker 
LocalCall1(size_t (* func)(void *),void * data)57*eb293b8fSAndroid Build Coastguard Worker size_t LocalCall1(size_t (*func)(void*), void* data) {
58*eb293b8fSAndroid Build Coastguard Worker   return LocalCall2(func, data);
59*eb293b8fSAndroid Build Coastguard Worker }
60*eb293b8fSAndroid Build Coastguard Worker 
Run(benchmark::State & state,size_t (* func)(void *),void * data)61*eb293b8fSAndroid Build Coastguard Worker static void Run(benchmark::State& state, size_t (*func)(void*), void* data) {
62*eb293b8fSAndroid Build Coastguard Worker   for (const auto& _ : state) {
63*eb293b8fSAndroid Build Coastguard Worker     if (LocalCall1(func, data) < 5) {
64*eb293b8fSAndroid Build Coastguard Worker       state.SkipWithError("Failed to unwind.");
65*eb293b8fSAndroid Build Coastguard Worker     }
66*eb293b8fSAndroid Build Coastguard Worker   }
67*eb293b8fSAndroid Build Coastguard Worker }
68*eb293b8fSAndroid Build Coastguard Worker 
Unwind(void * data_ptr)69*eb293b8fSAndroid Build Coastguard Worker static size_t Unwind(void* data_ptr) {
70*eb293b8fSAndroid Build Coastguard Worker   UnwindData* data = reinterpret_cast<UnwindData*>(data_ptr);
71*eb293b8fSAndroid Build Coastguard Worker   std::unique_ptr<unwindstack::Regs> regs(unwindstack::Regs::CreateFromLocal());
72*eb293b8fSAndroid Build Coastguard Worker   unwindstack::RegsGetLocal(regs.get());
73*eb293b8fSAndroid Build Coastguard Worker   unwindstack::Unwinder unwinder(kMaxFrames, data->maps, regs.get(), data->process_memory);
74*eb293b8fSAndroid Build Coastguard Worker   unwinder.SetResolveNames(data->resolve_names);
75*eb293b8fSAndroid Build Coastguard Worker   unwinder.Unwind();
76*eb293b8fSAndroid Build Coastguard Worker   return unwinder.NumFrames();
77*eb293b8fSAndroid Build Coastguard Worker }
78*eb293b8fSAndroid Build Coastguard Worker 
BM_local_unwind_uncached_process_memory(benchmark::State & state)79*eb293b8fSAndroid Build Coastguard Worker static void BM_local_unwind_uncached_process_memory(benchmark::State& state) {
80*eb293b8fSAndroid Build Coastguard Worker   auto process_memory = unwindstack::Memory::CreateProcessMemory(getpid());
81*eb293b8fSAndroid Build Coastguard Worker   unwindstack::LocalMaps maps;
82*eb293b8fSAndroid Build Coastguard Worker   if (!maps.Parse()) {
83*eb293b8fSAndroid Build Coastguard Worker     state.SkipWithError("Failed to parse local maps.");
84*eb293b8fSAndroid Build Coastguard Worker   }
85*eb293b8fSAndroid Build Coastguard Worker 
86*eb293b8fSAndroid Build Coastguard Worker   UnwindData data = {.process_memory = process_memory, .maps = &maps, .resolve_names = true};
87*eb293b8fSAndroid Build Coastguard Worker   Run(state, Unwind, &data);
88*eb293b8fSAndroid Build Coastguard Worker }
89*eb293b8fSAndroid Build Coastguard Worker BENCHMARK(BM_local_unwind_uncached_process_memory);
90*eb293b8fSAndroid Build Coastguard Worker 
BM_local_unwind_cached_process_memory(benchmark::State & state)91*eb293b8fSAndroid Build Coastguard Worker static void BM_local_unwind_cached_process_memory(benchmark::State& state) {
92*eb293b8fSAndroid Build Coastguard Worker   auto process_memory = unwindstack::Memory::CreateProcessMemoryCached(getpid());
93*eb293b8fSAndroid Build Coastguard Worker   unwindstack::LocalMaps maps;
94*eb293b8fSAndroid Build Coastguard Worker   if (!maps.Parse()) {
95*eb293b8fSAndroid Build Coastguard Worker     state.SkipWithError("Failed to parse local maps.");
96*eb293b8fSAndroid Build Coastguard Worker   }
97*eb293b8fSAndroid Build Coastguard Worker 
98*eb293b8fSAndroid Build Coastguard Worker   UnwindData data = {.process_memory = process_memory, .maps = &maps, .resolve_names = true};
99*eb293b8fSAndroid Build Coastguard Worker   Run(state, Unwind, &data);
100*eb293b8fSAndroid Build Coastguard Worker }
101*eb293b8fSAndroid Build Coastguard Worker BENCHMARK(BM_local_unwind_cached_process_memory);
102*eb293b8fSAndroid Build Coastguard Worker 
BM_local_android_unwind_uncached_process_memory(benchmark::State & state)103*eb293b8fSAndroid Build Coastguard Worker static void BM_local_android_unwind_uncached_process_memory(benchmark::State& state) {
104*eb293b8fSAndroid Build Coastguard Worker   auto process_memory = unwindstack::Memory::CreateProcessMemory(getpid());
105*eb293b8fSAndroid Build Coastguard Worker   unwindstack::AndroidLocalUnwinder unwinder(process_memory);
106*eb293b8fSAndroid Build Coastguard Worker   unwindstack::ErrorData error;
107*eb293b8fSAndroid Build Coastguard Worker   if (!unwinder.Initialize(error)) {
108*eb293b8fSAndroid Build Coastguard Worker     state.SkipWithError("Failed to initialize.");
109*eb293b8fSAndroid Build Coastguard Worker   }
110*eb293b8fSAndroid Build Coastguard Worker 
111*eb293b8fSAndroid Build Coastguard Worker   for (const auto& _ : state) {
112*eb293b8fSAndroid Build Coastguard Worker     if (LocalCall1(
113*eb293b8fSAndroid Build Coastguard Worker             [](void* u) -> size_t {
114*eb293b8fSAndroid Build Coastguard Worker               unwindstack::AndroidLocalUnwinder* unwinder =
115*eb293b8fSAndroid Build Coastguard Worker                   reinterpret_cast<unwindstack::AndroidLocalUnwinder*>(u);
116*eb293b8fSAndroid Build Coastguard Worker               unwindstack::AndroidUnwinderData data;
117*eb293b8fSAndroid Build Coastguard Worker               unwinder->Unwind(data);
118*eb293b8fSAndroid Build Coastguard Worker               return data.frames.size();
119*eb293b8fSAndroid Build Coastguard Worker             },
120*eb293b8fSAndroid Build Coastguard Worker             &unwinder) < 5) {
121*eb293b8fSAndroid Build Coastguard Worker       state.SkipWithError("Failed to unwind.");
122*eb293b8fSAndroid Build Coastguard Worker     }
123*eb293b8fSAndroid Build Coastguard Worker   }
124*eb293b8fSAndroid Build Coastguard Worker }
125*eb293b8fSAndroid Build Coastguard Worker BENCHMARK(BM_local_android_unwind_uncached_process_memory);
126*eb293b8fSAndroid Build Coastguard Worker 
BM_local_android_unwind_cached_process_memory(benchmark::State & state)127*eb293b8fSAndroid Build Coastguard Worker static void BM_local_android_unwind_cached_process_memory(benchmark::State& state) {
128*eb293b8fSAndroid Build Coastguard Worker   auto process_memory = unwindstack::Memory::CreateProcessMemoryCached(getpid());
129*eb293b8fSAndroid Build Coastguard Worker   unwindstack::AndroidLocalUnwinder unwinder(process_memory);
130*eb293b8fSAndroid Build Coastguard Worker   unwindstack::ErrorData error;
131*eb293b8fSAndroid Build Coastguard Worker   if (!unwinder.Initialize(error)) {
132*eb293b8fSAndroid Build Coastguard Worker     state.SkipWithError("Failed to initialize.");
133*eb293b8fSAndroid Build Coastguard Worker   }
134*eb293b8fSAndroid Build Coastguard Worker 
135*eb293b8fSAndroid Build Coastguard Worker   for (const auto& _ : state) {
136*eb293b8fSAndroid Build Coastguard Worker     if (LocalCall1(
137*eb293b8fSAndroid Build Coastguard Worker             [](void* u) -> size_t {
138*eb293b8fSAndroid Build Coastguard Worker               unwindstack::AndroidLocalUnwinder* unwinder =
139*eb293b8fSAndroid Build Coastguard Worker                   reinterpret_cast<unwindstack::AndroidLocalUnwinder*>(u);
140*eb293b8fSAndroid Build Coastguard Worker               unwindstack::AndroidUnwinderData data;
141*eb293b8fSAndroid Build Coastguard Worker               unwinder->Unwind(data);
142*eb293b8fSAndroid Build Coastguard Worker               return data.frames.size();
143*eb293b8fSAndroid Build Coastguard Worker             },
144*eb293b8fSAndroid Build Coastguard Worker             &unwinder) < 5) {
145*eb293b8fSAndroid Build Coastguard Worker       state.SkipWithError("Failed to unwind.");
146*eb293b8fSAndroid Build Coastguard Worker     }
147*eb293b8fSAndroid Build Coastguard Worker   }
148*eb293b8fSAndroid Build Coastguard Worker }
149*eb293b8fSAndroid Build Coastguard Worker BENCHMARK(BM_local_android_unwind_cached_process_memory);
150*eb293b8fSAndroid Build Coastguard Worker 
BM_local_unwind_local_updatable_maps_uncached(benchmark::State & state)151*eb293b8fSAndroid Build Coastguard Worker static void BM_local_unwind_local_updatable_maps_uncached(benchmark::State& state) {
152*eb293b8fSAndroid Build Coastguard Worker   auto process_memory = unwindstack::Memory::CreateProcessMemory(getpid());
153*eb293b8fSAndroid Build Coastguard Worker   unwindstack::LocalUpdatableMaps maps;
154*eb293b8fSAndroid Build Coastguard Worker   if (!maps.Parse()) {
155*eb293b8fSAndroid Build Coastguard Worker     state.SkipWithError("Failed to parse local maps.");
156*eb293b8fSAndroid Build Coastguard Worker   }
157*eb293b8fSAndroid Build Coastguard Worker 
158*eb293b8fSAndroid Build Coastguard Worker   UnwindData data = {.process_memory = process_memory, .maps = &maps, .resolve_names = true};
159*eb293b8fSAndroid Build Coastguard Worker   Run(state, Unwind, &data);
160*eb293b8fSAndroid Build Coastguard Worker }
161*eb293b8fSAndroid Build Coastguard Worker BENCHMARK(BM_local_unwind_local_updatable_maps_uncached);
162*eb293b8fSAndroid Build Coastguard Worker 
BM_local_unwind_local_updatable_maps_cached(benchmark::State & state)163*eb293b8fSAndroid Build Coastguard Worker static void BM_local_unwind_local_updatable_maps_cached(benchmark::State& state) {
164*eb293b8fSAndroid Build Coastguard Worker   auto process_memory = unwindstack::Memory::CreateProcessMemoryCached(getpid());
165*eb293b8fSAndroid Build Coastguard Worker   unwindstack::LocalUpdatableMaps maps;
166*eb293b8fSAndroid Build Coastguard Worker   if (!maps.Parse()) {
167*eb293b8fSAndroid Build Coastguard Worker     state.SkipWithError("Failed to parse local maps.");
168*eb293b8fSAndroid Build Coastguard Worker   }
169*eb293b8fSAndroid Build Coastguard Worker 
170*eb293b8fSAndroid Build Coastguard Worker   UnwindData data = {.process_memory = process_memory, .maps = &maps, .resolve_names = true};
171*eb293b8fSAndroid Build Coastguard Worker   Run(state, Unwind, &data);
172*eb293b8fSAndroid Build Coastguard Worker }
173*eb293b8fSAndroid Build Coastguard Worker BENCHMARK(BM_local_unwind_local_updatable_maps_cached);
174*eb293b8fSAndroid Build Coastguard Worker 
BM_local_unwind_local_updatable_maps_thread_cached(benchmark::State & state)175*eb293b8fSAndroid Build Coastguard Worker static void BM_local_unwind_local_updatable_maps_thread_cached(benchmark::State& state) {
176*eb293b8fSAndroid Build Coastguard Worker   auto process_memory = unwindstack::Memory::CreateProcessMemoryThreadCached(getpid());
177*eb293b8fSAndroid Build Coastguard Worker   unwindstack::LocalUpdatableMaps maps;
178*eb293b8fSAndroid Build Coastguard Worker   if (!maps.Parse()) {
179*eb293b8fSAndroid Build Coastguard Worker     state.SkipWithError("Failed to parse local maps.");
180*eb293b8fSAndroid Build Coastguard Worker   }
181*eb293b8fSAndroid Build Coastguard Worker 
182*eb293b8fSAndroid Build Coastguard Worker   UnwindData data = {.process_memory = process_memory, .maps = &maps, .resolve_names = true};
183*eb293b8fSAndroid Build Coastguard Worker   Run(state, Unwind, &data);
184*eb293b8fSAndroid Build Coastguard Worker }
185*eb293b8fSAndroid Build Coastguard Worker BENCHMARK(BM_local_unwind_local_updatable_maps_thread_cached);
186*eb293b8fSAndroid Build Coastguard Worker 
BM_local_unwind_uncached_process_memory_no_func_names(benchmark::State & state)187*eb293b8fSAndroid Build Coastguard Worker static void BM_local_unwind_uncached_process_memory_no_func_names(benchmark::State& state) {
188*eb293b8fSAndroid Build Coastguard Worker   auto process_memory = unwindstack::Memory::CreateProcessMemory(getpid());
189*eb293b8fSAndroid Build Coastguard Worker   unwindstack::LocalMaps maps;
190*eb293b8fSAndroid Build Coastguard Worker   if (!maps.Parse()) {
191*eb293b8fSAndroid Build Coastguard Worker     state.SkipWithError("Failed to parse local maps.");
192*eb293b8fSAndroid Build Coastguard Worker   }
193*eb293b8fSAndroid Build Coastguard Worker 
194*eb293b8fSAndroid Build Coastguard Worker   UnwindData data = {.process_memory = process_memory, .maps = &maps, .resolve_names = false};
195*eb293b8fSAndroid Build Coastguard Worker   Run(state, Unwind, &data);
196*eb293b8fSAndroid Build Coastguard Worker }
197*eb293b8fSAndroid Build Coastguard Worker BENCHMARK(BM_local_unwind_uncached_process_memory_no_func_names);
198*eb293b8fSAndroid Build Coastguard Worker 
BM_local_unwind_cached_process_memory_no_func_names(benchmark::State & state)199*eb293b8fSAndroid Build Coastguard Worker static void BM_local_unwind_cached_process_memory_no_func_names(benchmark::State& state) {
200*eb293b8fSAndroid Build Coastguard Worker   auto process_memory = unwindstack::Memory::CreateProcessMemoryCached(getpid());
201*eb293b8fSAndroid Build Coastguard Worker   unwindstack::LocalMaps maps;
202*eb293b8fSAndroid Build Coastguard Worker   if (!maps.Parse()) {
203*eb293b8fSAndroid Build Coastguard Worker     state.SkipWithError("Failed to parse local maps.");
204*eb293b8fSAndroid Build Coastguard Worker   }
205*eb293b8fSAndroid Build Coastguard Worker 
206*eb293b8fSAndroid Build Coastguard Worker   UnwindData data = {.process_memory = process_memory, .maps = &maps, .resolve_names = false};
207*eb293b8fSAndroid Build Coastguard Worker   Run(state, Unwind, &data);
208*eb293b8fSAndroid Build Coastguard Worker }
209*eb293b8fSAndroid Build Coastguard Worker BENCHMARK(BM_local_unwind_cached_process_memory_no_func_names);
210*eb293b8fSAndroid Build Coastguard Worker 
BM_local_unwind_local_updatable_maps_uncached_no_func_names(benchmark::State & state)211*eb293b8fSAndroid Build Coastguard Worker static void BM_local_unwind_local_updatable_maps_uncached_no_func_names(benchmark::State& state) {
212*eb293b8fSAndroid Build Coastguard Worker   auto process_memory = unwindstack::Memory::CreateProcessMemory(getpid());
213*eb293b8fSAndroid Build Coastguard Worker   unwindstack::LocalUpdatableMaps maps;
214*eb293b8fSAndroid Build Coastguard Worker   if (!maps.Parse()) {
215*eb293b8fSAndroid Build Coastguard Worker     state.SkipWithError("Failed to parse local maps.");
216*eb293b8fSAndroid Build Coastguard Worker   }
217*eb293b8fSAndroid Build Coastguard Worker 
218*eb293b8fSAndroid Build Coastguard Worker   UnwindData data = {.process_memory = process_memory, .maps = &maps, .resolve_names = false};
219*eb293b8fSAndroid Build Coastguard Worker   Run(state, Unwind, &data);
220*eb293b8fSAndroid Build Coastguard Worker }
221*eb293b8fSAndroid Build Coastguard Worker BENCHMARK(BM_local_unwind_local_updatable_maps_uncached_no_func_names);
222*eb293b8fSAndroid Build Coastguard Worker 
BM_local_unwind_local_updatable_maps_cached_no_func_names(benchmark::State & state)223*eb293b8fSAndroid Build Coastguard Worker static void BM_local_unwind_local_updatable_maps_cached_no_func_names(benchmark::State& state) {
224*eb293b8fSAndroid Build Coastguard Worker   auto process_memory = unwindstack::Memory::CreateProcessMemoryCached(getpid());
225*eb293b8fSAndroid Build Coastguard Worker   unwindstack::LocalUpdatableMaps maps;
226*eb293b8fSAndroid Build Coastguard Worker   if (!maps.Parse()) {
227*eb293b8fSAndroid Build Coastguard Worker     state.SkipWithError("Failed to parse local maps.");
228*eb293b8fSAndroid Build Coastguard Worker   }
229*eb293b8fSAndroid Build Coastguard Worker 
230*eb293b8fSAndroid Build Coastguard Worker   UnwindData data = {.process_memory = process_memory, .maps = &maps, .resolve_names = false};
231*eb293b8fSAndroid Build Coastguard Worker   Run(state, Unwind, &data);
232*eb293b8fSAndroid Build Coastguard Worker }
233*eb293b8fSAndroid Build Coastguard Worker BENCHMARK(BM_local_unwind_local_updatable_maps_cached_no_func_names);
234*eb293b8fSAndroid Build Coastguard Worker 
BM_local_unwind_uncached_process_memory_unsafe_reads(benchmark::State & state)235*eb293b8fSAndroid Build Coastguard Worker static void BM_local_unwind_uncached_process_memory_unsafe_reads(benchmark::State& state) {
236*eb293b8fSAndroid Build Coastguard Worker   std::shared_ptr<unwindstack::Memory> process_memory(new unwindstack::MemoryLocalUnsafe());
237*eb293b8fSAndroid Build Coastguard Worker   unwindstack::LocalMaps maps;
238*eb293b8fSAndroid Build Coastguard Worker   if (!maps.Parse()) {
239*eb293b8fSAndroid Build Coastguard Worker     state.SkipWithError("Failed to parse local maps.");
240*eb293b8fSAndroid Build Coastguard Worker   }
241*eb293b8fSAndroid Build Coastguard Worker 
242*eb293b8fSAndroid Build Coastguard Worker   UnwindData data = {.process_memory = process_memory, .maps = &maps, .resolve_names = true};
243*eb293b8fSAndroid Build Coastguard Worker   Run(state, Unwind, &data);
244*eb293b8fSAndroid Build Coastguard Worker }
245*eb293b8fSAndroid Build Coastguard Worker BENCHMARK(BM_local_unwind_uncached_process_memory_unsafe_reads);
246