1 // Copyright (c) 2010 Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 // * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 //
30 // fast_source_line_resolver_unittest.cc: Unit tests for FastSourceLineResolver.
31 // Two different approaches for testing fast source line resolver:
32 // First, use the same unit test data for basic source line resolver.
33 // Second, read data from symbol files, load them as basic modules, and then
34 // serialize them and load the serialized data as fast modules. Then compare
35 // modules to assure the fast module contains exactly the same data as
36 // basic module.
37 //
38 // Author: Siyang Xie (lambxsy@google.com)
39
40 #include <assert.h>
41 #include <stdio.h>
42
43 #include <sstream>
44 #include <string>
45
46 #include "breakpad_googletest_includes.h"
47 #include "common/using_std_string.h"
48 #include "google_breakpad/processor/code_module.h"
49 #include "google_breakpad/processor/stack_frame.h"
50 #include "google_breakpad/processor/memory_region.h"
51 #include "processor/logging.h"
52 #include "processor/module_serializer.h"
53 #include "processor/module_comparer.h"
54
55 namespace {
56
57 using google_breakpad::SourceLineResolverBase;
58 using google_breakpad::BasicSourceLineResolver;
59 using google_breakpad::FastSourceLineResolver;
60 using google_breakpad::ModuleSerializer;
61 using google_breakpad::ModuleComparer;
62 using google_breakpad::CFIFrameInfo;
63 using google_breakpad::CodeModule;
64 using google_breakpad::MemoryRegion;
65 using google_breakpad::StackFrame;
66 using google_breakpad::WindowsFrameInfo;
67 using google_breakpad::linked_ptr;
68 using google_breakpad::scoped_ptr;
69
70 class TestCodeModule : public CodeModule {
71 public:
TestCodeModule(string code_file)72 explicit TestCodeModule(string code_file) : code_file_(code_file) {}
~TestCodeModule()73 virtual ~TestCodeModule() {}
74
base_address() const75 virtual uint64_t base_address() const { return 0; }
size() const76 virtual uint64_t size() const { return 0xb000; }
code_file() const77 virtual string code_file() const { return code_file_; }
code_identifier() const78 virtual string code_identifier() const { return ""; }
debug_file() const79 virtual string debug_file() const { return ""; }
debug_identifier() const80 virtual string debug_identifier() const { return ""; }
version() const81 virtual string version() const { return ""; }
Copy() const82 virtual CodeModule* Copy() const {
83 return new TestCodeModule(code_file_);
84 }
is_unloaded() const85 virtual bool is_unloaded() const { return false; }
shrink_down_delta() const86 virtual uint64_t shrink_down_delta() const { return 0; }
SetShrinkDownDelta(uint64_t shrink_down_delta)87 virtual void SetShrinkDownDelta(uint64_t shrink_down_delta) {}
88
89 private:
90 string code_file_;
91 };
92
93 // A mock memory region object, for use by the STACK CFI tests.
94 class MockMemoryRegion: public MemoryRegion {
GetBase() const95 uint64_t GetBase() const { return 0x10000; }
GetSize() const96 uint32_t GetSize() const { return 0x01000; }
GetMemoryAtAddress(uint64_t address,uint8_t * value) const97 bool GetMemoryAtAddress(uint64_t address, uint8_t *value) const {
98 *value = address & 0xff;
99 return true;
100 }
GetMemoryAtAddress(uint64_t address,uint16_t * value) const101 bool GetMemoryAtAddress(uint64_t address, uint16_t *value) const {
102 *value = address & 0xffff;
103 return true;
104 }
GetMemoryAtAddress(uint64_t address,uint32_t * value) const105 bool GetMemoryAtAddress(uint64_t address, uint32_t *value) const {
106 switch (address) {
107 case 0x10008: *value = 0x98ecadc3; break; // saved %ebx
108 case 0x1000c: *value = 0x878f7524; break; // saved %esi
109 case 0x10010: *value = 0x6312f9a5; break; // saved %edi
110 case 0x10014: *value = 0x10038; break; // caller's %ebp
111 case 0x10018: *value = 0xf6438648; break; // return address
112 default: *value = 0xdeadbeef; break; // junk
113 }
114 return true;
115 }
GetMemoryAtAddress(uint64_t address,uint64_t * value) const116 bool GetMemoryAtAddress(uint64_t address, uint64_t *value) const {
117 *value = address;
118 return true;
119 }
Print() const120 void Print() const {
121 assert(false);
122 }
123 };
124
125 // Verify that, for every association in ACTUAL, EXPECTED has the same
126 // association. (That is, ACTUAL's associations should be a subset of
127 // EXPECTED's.) Also verify that ACTUAL has associations for ".ra" and
128 // ".cfa".
VerifyRegisters(const char * file,int line,const CFIFrameInfo::RegisterValueMap<uint32_t> & expected,const CFIFrameInfo::RegisterValueMap<uint32_t> & actual)129 static bool VerifyRegisters(
130 const char *file, int line,
131 const CFIFrameInfo::RegisterValueMap<uint32_t> &expected,
132 const CFIFrameInfo::RegisterValueMap<uint32_t> &actual) {
133 CFIFrameInfo::RegisterValueMap<uint32_t>::const_iterator a;
134 a = actual.find(".cfa");
135 if (a == actual.end())
136 return false;
137 a = actual.find(".ra");
138 if (a == actual.end())
139 return false;
140 for (a = actual.begin(); a != actual.end(); a++) {
141 CFIFrameInfo::RegisterValueMap<uint32_t>::const_iterator e =
142 expected.find(a->first);
143 if (e == expected.end()) {
144 fprintf(stderr, "%s:%d: unexpected register '%s' recovered, value 0x%x\n",
145 file, line, a->first.c_str(), a->second);
146 return false;
147 }
148 if (e->second != a->second) {
149 fprintf(stderr,
150 "%s:%d: register '%s' recovered value was 0x%x, expected 0x%x\n",
151 file, line, a->first.c_str(), a->second, e->second);
152 return false;
153 }
154 // Don't complain if this doesn't recover all registers. Although
155 // the DWARF spec says that unmentioned registers are undefined,
156 // GCC uses omission to mean that they are unchanged.
157 }
158 return true;
159 }
160
VerifyEmpty(const StackFrame & frame)161 static bool VerifyEmpty(const StackFrame &frame) {
162 if (frame.function_name.empty() &&
163 frame.source_file_name.empty() &&
164 frame.source_line == 0)
165 return true;
166 return false;
167 }
168
ClearSourceLineInfo(StackFrame * frame)169 static void ClearSourceLineInfo(StackFrame *frame) {
170 frame->function_name.clear();
171 frame->module = NULL;
172 frame->source_file_name.clear();
173 frame->source_line = 0;
174 }
175
176 class TestFastSourceLineResolver : public ::testing::Test {
177 public:
SetUp()178 void SetUp() {
179 testdata_dir = string(getenv("srcdir") ? getenv("srcdir") : ".") +
180 "/src/processor/testdata";
181 }
182
symbol_file(int file_index)183 string symbol_file(int file_index) {
184 std::stringstream ss;
185 ss << testdata_dir << "/module" << file_index << ".out";
186 return ss.str();
187 }
188
189 ModuleSerializer serializer;
190 BasicSourceLineResolver basic_resolver;
191 FastSourceLineResolver fast_resolver;
192 ModuleComparer module_comparer;
193
194 string testdata_dir;
195 };
196
197 // Test adapted from basic_source_line_resolver_unittest.
TEST_F(TestFastSourceLineResolver,TestLoadAndResolve)198 TEST_F(TestFastSourceLineResolver, TestLoadAndResolve) {
199 TestCodeModule module1("module1");
200 ASSERT_TRUE(basic_resolver.LoadModule(&module1, symbol_file(1)));
201 ASSERT_TRUE(basic_resolver.HasModule(&module1));
202 // Convert module1 to fast_module:
203 ASSERT_TRUE(serializer.ConvertOneModule(
204 module1.code_file(), &basic_resolver, &fast_resolver));
205 ASSERT_TRUE(fast_resolver.HasModule(&module1));
206
207 TestCodeModule module2("module2");
208 ASSERT_TRUE(basic_resolver.LoadModule(&module2, symbol_file(2)));
209 ASSERT_TRUE(basic_resolver.HasModule(&module2));
210 // Convert module2 to fast_module:
211 ASSERT_TRUE(serializer.ConvertOneModule(
212 module2.code_file(), &basic_resolver, &fast_resolver));
213 ASSERT_TRUE(fast_resolver.HasModule(&module2));
214
215 StackFrame frame;
216 scoped_ptr<WindowsFrameInfo> windows_frame_info;
217 scoped_ptr<CFIFrameInfo> cfi_frame_info;
218 frame.instruction = 0x1000;
219 frame.module = NULL;
220 fast_resolver.FillSourceLineInfo(&frame);
221 ASSERT_FALSE(frame.module);
222 ASSERT_TRUE(frame.function_name.empty());
223 ASSERT_EQ(frame.function_base, 0U);
224 ASSERT_TRUE(frame.source_file_name.empty());
225 ASSERT_EQ(frame.source_line, 0);
226 ASSERT_EQ(frame.source_line_base, 0U);
227
228 frame.module = &module1;
229 fast_resolver.FillSourceLineInfo(&frame);
230 ASSERT_EQ(frame.function_name, "Function1_1");
231 ASSERT_TRUE(frame.module);
232 ASSERT_EQ(frame.module->code_file(), "module1");
233 ASSERT_EQ(frame.function_base, 0x1000U);
234 ASSERT_EQ(frame.source_file_name, "file1_1.cc");
235 ASSERT_EQ(frame.source_line, 44);
236 ASSERT_EQ(frame.source_line_base, 0x1000U);
237 windows_frame_info.reset(fast_resolver.FindWindowsFrameInfo(&frame));
238 ASSERT_TRUE(windows_frame_info.get());
239 ASSERT_FALSE(windows_frame_info->allocates_base_pointer);
240 ASSERT_EQ(windows_frame_info->program_string,
241 "$eip 4 + ^ = $esp $ebp 8 + = $ebp $ebp ^ =");
242
243 ClearSourceLineInfo(&frame);
244 frame.instruction = 0x800;
245 frame.module = &module1;
246 fast_resolver.FillSourceLineInfo(&frame);
247 ASSERT_TRUE(VerifyEmpty(frame));
248 windows_frame_info.reset(fast_resolver.FindWindowsFrameInfo(&frame));
249 ASSERT_FALSE(windows_frame_info.get());
250
251 frame.instruction = 0x1280;
252 fast_resolver.FillSourceLineInfo(&frame);
253 ASSERT_EQ(frame.function_name, "Function1_3");
254 ASSERT_TRUE(frame.source_file_name.empty());
255 ASSERT_EQ(frame.source_line, 0);
256 windows_frame_info.reset(fast_resolver.FindWindowsFrameInfo(&frame));
257 ASSERT_TRUE(windows_frame_info.get());
258 ASSERT_EQ(windows_frame_info->type_, WindowsFrameInfo::STACK_INFO_UNKNOWN);
259 ASSERT_FALSE(windows_frame_info->allocates_base_pointer);
260 ASSERT_TRUE(windows_frame_info->program_string.empty());
261
262 frame.instruction = 0x1380;
263 fast_resolver.FillSourceLineInfo(&frame);
264 ASSERT_EQ(frame.function_name, "Function1_4");
265 ASSERT_TRUE(frame.source_file_name.empty());
266 ASSERT_EQ(frame.source_line, 0);
267 windows_frame_info.reset(fast_resolver.FindWindowsFrameInfo(&frame));
268 ASSERT_TRUE(windows_frame_info.get());
269 ASSERT_EQ(windows_frame_info->type_, WindowsFrameInfo::STACK_INFO_FRAME_DATA);
270 ASSERT_FALSE(windows_frame_info->allocates_base_pointer);
271 ASSERT_FALSE(windows_frame_info->program_string.empty());
272
273 frame.instruction = 0x2000;
274 windows_frame_info.reset(fast_resolver.FindWindowsFrameInfo(&frame));
275 ASSERT_FALSE(windows_frame_info.get());
276
277 // module1 has STACK CFI records covering 3d40..3def;
278 // module2 has STACK CFI records covering 3df0..3e9f;
279 // check that FindCFIFrameInfo doesn't claim to find any outside those ranges.
280 frame.instruction = 0x3d3f;
281 frame.module = &module1;
282 cfi_frame_info.reset(fast_resolver.FindCFIFrameInfo(&frame));
283 ASSERT_FALSE(cfi_frame_info.get());
284
285 frame.instruction = 0x3e9f;
286 frame.module = &module1;
287 cfi_frame_info.reset(fast_resolver.FindCFIFrameInfo(&frame));
288 ASSERT_FALSE(cfi_frame_info.get());
289
290 CFIFrameInfo::RegisterValueMap<uint32_t> current_registers;
291 CFIFrameInfo::RegisterValueMap<uint32_t> caller_registers;
292 CFIFrameInfo::RegisterValueMap<uint32_t> expected_caller_registers;
293 MockMemoryRegion memory;
294
295 // Regardless of which instruction evaluation takes place at, it
296 // should produce the same values for the caller's registers.
297 expected_caller_registers[".cfa"] = 0x1001c;
298 expected_caller_registers[".ra"] = 0xf6438648;
299 expected_caller_registers["$ebp"] = 0x10038;
300 expected_caller_registers["$ebx"] = 0x98ecadc3;
301 expected_caller_registers["$esi"] = 0x878f7524;
302 expected_caller_registers["$edi"] = 0x6312f9a5;
303
304 frame.instruction = 0x3d40;
305 frame.module = &module1;
306 current_registers.clear();
307 current_registers["$esp"] = 0x10018;
308 current_registers["$ebp"] = 0x10038;
309 current_registers["$ebx"] = 0x98ecadc3;
310 current_registers["$esi"] = 0x878f7524;
311 current_registers["$edi"] = 0x6312f9a5;
312 cfi_frame_info.reset(fast_resolver.FindCFIFrameInfo(&frame));
313 ASSERT_TRUE(cfi_frame_info.get());
314 ASSERT_TRUE(cfi_frame_info.get()
315 ->FindCallerRegs<uint32_t>(current_registers, memory,
316 &caller_registers));
317 ASSERT_TRUE(VerifyRegisters(__FILE__, __LINE__,
318 expected_caller_registers, caller_registers));
319
320 frame.instruction = 0x3d41;
321 current_registers["$esp"] = 0x10014;
322 cfi_frame_info.reset(fast_resolver.FindCFIFrameInfo(&frame));
323 ASSERT_TRUE(cfi_frame_info.get());
324 ASSERT_TRUE(cfi_frame_info.get()
325 ->FindCallerRegs<uint32_t>(current_registers, memory,
326 &caller_registers));
327 ASSERT_TRUE(VerifyRegisters(__FILE__, __LINE__,
328 expected_caller_registers, caller_registers));
329
330 frame.instruction = 0x3d43;
331 current_registers["$ebp"] = 0x10014;
332 cfi_frame_info.reset(fast_resolver.FindCFIFrameInfo(&frame));
333 ASSERT_TRUE(cfi_frame_info.get());
334 ASSERT_TRUE(cfi_frame_info.get()
335 ->FindCallerRegs<uint32_t>(current_registers, memory,
336 &caller_registers));
337 VerifyRegisters(__FILE__, __LINE__,
338 expected_caller_registers, caller_registers);
339
340 frame.instruction = 0x3d54;
341 current_registers["$ebx"] = 0x6864f054U;
342 cfi_frame_info.reset(fast_resolver.FindCFIFrameInfo(&frame));
343 ASSERT_TRUE(cfi_frame_info.get());
344 ASSERT_TRUE(cfi_frame_info.get()
345 ->FindCallerRegs<uint32_t>(current_registers, memory,
346 &caller_registers));
347 VerifyRegisters(__FILE__, __LINE__,
348 expected_caller_registers, caller_registers);
349
350 frame.instruction = 0x3d5a;
351 current_registers["$esi"] = 0x6285f79aU;
352 cfi_frame_info.reset(fast_resolver.FindCFIFrameInfo(&frame));
353 ASSERT_TRUE(cfi_frame_info.get());
354 ASSERT_TRUE(cfi_frame_info.get()
355 ->FindCallerRegs<uint32_t>(current_registers, memory,
356 &caller_registers));
357 VerifyRegisters(__FILE__, __LINE__,
358 expected_caller_registers, caller_registers);
359
360 frame.instruction = 0x3d84;
361 current_registers["$edi"] = 0x64061449U;
362 cfi_frame_info.reset(fast_resolver.FindCFIFrameInfo(&frame));
363 ASSERT_TRUE(cfi_frame_info.get());
364 ASSERT_TRUE(cfi_frame_info.get()
365 ->FindCallerRegs<uint32_t>(current_registers, memory,
366 &caller_registers));
367 VerifyRegisters(__FILE__, __LINE__,
368 expected_caller_registers, caller_registers);
369
370 frame.instruction = 0x2900;
371 frame.module = &module1;
372 fast_resolver.FillSourceLineInfo(&frame);
373 ASSERT_EQ(frame.function_name, string("PublicSymbol"));
374
375 frame.instruction = 0x4000;
376 frame.module = &module1;
377 fast_resolver.FillSourceLineInfo(&frame);
378 ASSERT_EQ(frame.function_name, string("LargeFunction"));
379
380 frame.instruction = 0x2181;
381 frame.module = &module2;
382 fast_resolver.FillSourceLineInfo(&frame);
383 ASSERT_EQ(frame.function_name, "Function2_2");
384 ASSERT_EQ(frame.function_base, 0x2170U);
385 ASSERT_TRUE(frame.module);
386 ASSERT_EQ(frame.module->code_file(), "module2");
387 ASSERT_EQ(frame.source_file_name, "file2_2.cc");
388 ASSERT_EQ(frame.source_line, 21);
389 ASSERT_EQ(frame.source_line_base, 0x2180U);
390 windows_frame_info.reset(fast_resolver.FindWindowsFrameInfo(&frame));
391 ASSERT_TRUE(windows_frame_info.get());
392 ASSERT_EQ(windows_frame_info->type_, WindowsFrameInfo::STACK_INFO_FRAME_DATA);
393 ASSERT_EQ(windows_frame_info->prolog_size, 1U);
394
395 frame.instruction = 0x216f;
396 fast_resolver.FillSourceLineInfo(&frame);
397 ASSERT_EQ(frame.function_name, "Public2_1");
398
399 ClearSourceLineInfo(&frame);
400 frame.instruction = 0x219f;
401 frame.module = &module2;
402 fast_resolver.FillSourceLineInfo(&frame);
403 ASSERT_TRUE(frame.function_name.empty());
404
405 frame.instruction = 0x21a0;
406 frame.module = &module2;
407 fast_resolver.FillSourceLineInfo(&frame);
408 ASSERT_EQ(frame.function_name, "Public2_2");
409 }
410
TEST_F(TestFastSourceLineResolver,TestInvalidLoads)411 TEST_F(TestFastSourceLineResolver, TestInvalidLoads) {
412 TestCodeModule module3("module3");
413 ASSERT_TRUE(basic_resolver.LoadModule(&module3,
414 testdata_dir + "/module3_bad.out"));
415 ASSERT_TRUE(basic_resolver.HasModule(&module3));
416 ASSERT_TRUE(basic_resolver.IsModuleCorrupt(&module3));
417 // Convert module3 to fast_module:
418 ASSERT_TRUE(serializer.ConvertOneModule(module3.code_file(),
419 &basic_resolver,
420 &fast_resolver));
421 ASSERT_TRUE(fast_resolver.HasModule(&module3));
422 ASSERT_TRUE(fast_resolver.IsModuleCorrupt(&module3));
423
424 TestCodeModule module4("module4");
425 ASSERT_TRUE(basic_resolver.LoadModule(&module4,
426 testdata_dir + "/module4_bad.out"));
427 ASSERT_TRUE(basic_resolver.HasModule(&module4));
428 ASSERT_TRUE(basic_resolver.IsModuleCorrupt(&module4));
429 // Convert module4 to fast_module:
430 ASSERT_TRUE(serializer.ConvertOneModule(module4.code_file(),
431 &basic_resolver,
432 &fast_resolver));
433 ASSERT_TRUE(fast_resolver.HasModule(&module4));
434 ASSERT_TRUE(fast_resolver.IsModuleCorrupt(&module4));
435
436 TestCodeModule module5("module5");
437 ASSERT_FALSE(fast_resolver.LoadModule(&module5,
438 testdata_dir + "/invalid-filename"));
439 ASSERT_FALSE(fast_resolver.HasModule(&module5));
440
441 TestCodeModule invalidmodule("invalid-module");
442 ASSERT_FALSE(fast_resolver.HasModule(&invalidmodule));
443 }
444
TEST_F(TestFastSourceLineResolver,TestUnload)445 TEST_F(TestFastSourceLineResolver, TestUnload) {
446 TestCodeModule module1("module1");
447 ASSERT_FALSE(basic_resolver.HasModule(&module1));
448
449 ASSERT_TRUE(basic_resolver.LoadModule(&module1, symbol_file(1)));
450 ASSERT_TRUE(basic_resolver.HasModule(&module1));
451 // Convert module1 to fast_module.
452 ASSERT_TRUE(serializer.ConvertOneModule(module1.code_file(),
453 &basic_resolver,
454 &fast_resolver));
455 ASSERT_TRUE(fast_resolver.HasModule(&module1));
456 basic_resolver.UnloadModule(&module1);
457 fast_resolver.UnloadModule(&module1);
458 ASSERT_FALSE(fast_resolver.HasModule(&module1));
459
460 ASSERT_TRUE(basic_resolver.LoadModule(&module1, symbol_file(1)));
461 ASSERT_TRUE(basic_resolver.HasModule(&module1));
462 // Convert module1 to fast_module.
463 ASSERT_TRUE(serializer.ConvertOneModule(module1.code_file(),
464 &basic_resolver,
465 &fast_resolver));
466 ASSERT_TRUE(fast_resolver.HasModule(&module1));
467 }
468
TEST_F(TestFastSourceLineResolver,CompareModule)469 TEST_F(TestFastSourceLineResolver, CompareModule) {
470 char *symbol_data;
471 size_t symbol_data_size;
472 string symbol_data_string;
473 string filename;
474
475 for (int module_index = 0; module_index < 3; ++module_index) {
476 std::stringstream ss;
477 ss << testdata_dir << "/module" << module_index << ".out";
478 filename = ss.str();
479 ASSERT_TRUE(SourceLineResolverBase::ReadSymbolFile(
480 symbol_file(module_index), &symbol_data, &symbol_data_size));
481 symbol_data_string.assign(symbol_data, symbol_data_size);
482 delete [] symbol_data;
483 ASSERT_TRUE(module_comparer.Compare(symbol_data_string));
484 }
485 }
486
487 } // namespace
488
main(int argc,char * argv[])489 int main(int argc, char *argv[]) {
490 ::testing::InitGoogleTest(&argc, argv);
491 return RUN_ALL_TESTS();
492 }
493