Mercurial > hg > CbC > CbC_llvm
comparison lldb/source/Core/EmulateInstruction.cpp @ 150:1d019706d866
LLVM10
author | anatofuz |
---|---|
date | Thu, 13 Feb 2020 15:10:13 +0900 |
parents | |
children | 2e18cbf3894f |
comparison
equal
deleted
inserted
replaced
147:c2174574ed3a | 150:1d019706d866 |
---|---|
1 //===-- EmulateInstruction.cpp --------------------------------------------===// | |
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/Core/EmulateInstruction.h" | |
10 | |
11 #include "lldb/Core/Address.h" | |
12 #include "lldb/Core/DumpRegisterValue.h" | |
13 #include "lldb/Core/PluginManager.h" | |
14 #include "lldb/Core/StreamFile.h" | |
15 #include "lldb/Symbol/UnwindPlan.h" | |
16 #include "lldb/Target/Process.h" | |
17 #include "lldb/Target/RegisterContext.h" | |
18 #include "lldb/Target/StackFrame.h" | |
19 #include "lldb/Utility/ConstString.h" | |
20 #include "lldb/Utility/DataExtractor.h" | |
21 #include "lldb/Utility/RegisterValue.h" | |
22 #include "lldb/Utility/Status.h" | |
23 #include "lldb/Utility/Stream.h" | |
24 #include "lldb/Utility/StreamString.h" | |
25 #include "lldb/lldb-forward.h" | |
26 #include "lldb/lldb-private-interfaces.h" | |
27 | |
28 #include "llvm/ADT/StringRef.h" | |
29 | |
30 #include <cstring> | |
31 #include <memory> | |
32 | |
33 #include <inttypes.h> | |
34 #include <stdio.h> | |
35 | |
36 namespace lldb_private { | |
37 class Target; | |
38 } | |
39 | |
40 using namespace lldb; | |
41 using namespace lldb_private; | |
42 | |
43 EmulateInstruction * | |
44 EmulateInstruction::FindPlugin(const ArchSpec &arch, | |
45 InstructionType supported_inst_type, | |
46 const char *plugin_name) { | |
47 EmulateInstructionCreateInstance create_callback = nullptr; | |
48 if (plugin_name) { | |
49 ConstString const_plugin_name(plugin_name); | |
50 create_callback = | |
51 PluginManager::GetEmulateInstructionCreateCallbackForPluginName( | |
52 const_plugin_name); | |
53 if (create_callback) { | |
54 EmulateInstruction *emulate_insn_ptr = | |
55 create_callback(arch, supported_inst_type); | |
56 if (emulate_insn_ptr) | |
57 return emulate_insn_ptr; | |
58 } | |
59 } else { | |
60 for (uint32_t idx = 0; | |
61 (create_callback = | |
62 PluginManager::GetEmulateInstructionCreateCallbackAtIndex(idx)) != | |
63 nullptr; | |
64 ++idx) { | |
65 EmulateInstruction *emulate_insn_ptr = | |
66 create_callback(arch, supported_inst_type); | |
67 if (emulate_insn_ptr) | |
68 return emulate_insn_ptr; | |
69 } | |
70 } | |
71 return nullptr; | |
72 } | |
73 | |
74 EmulateInstruction::EmulateInstruction(const ArchSpec &arch) : m_arch(arch) {} | |
75 | |
76 bool EmulateInstruction::ReadRegister(const RegisterInfo *reg_info, | |
77 RegisterValue ®_value) { | |
78 if (m_read_reg_callback != nullptr) | |
79 return m_read_reg_callback(this, m_baton, reg_info, reg_value); | |
80 return false; | |
81 } | |
82 | |
83 bool EmulateInstruction::ReadRegister(lldb::RegisterKind reg_kind, | |
84 uint32_t reg_num, | |
85 RegisterValue ®_value) { | |
86 RegisterInfo reg_info; | |
87 if (GetRegisterInfo(reg_kind, reg_num, reg_info)) | |
88 return ReadRegister(®_info, reg_value); | |
89 return false; | |
90 } | |
91 | |
92 uint64_t EmulateInstruction::ReadRegisterUnsigned(lldb::RegisterKind reg_kind, | |
93 uint32_t reg_num, | |
94 uint64_t fail_value, | |
95 bool *success_ptr) { | |
96 RegisterValue reg_value; | |
97 if (ReadRegister(reg_kind, reg_num, reg_value)) | |
98 return reg_value.GetAsUInt64(fail_value, success_ptr); | |
99 if (success_ptr) | |
100 *success_ptr = false; | |
101 return fail_value; | |
102 } | |
103 | |
104 uint64_t EmulateInstruction::ReadRegisterUnsigned(const RegisterInfo *reg_info, | |
105 uint64_t fail_value, | |
106 bool *success_ptr) { | |
107 RegisterValue reg_value; | |
108 if (ReadRegister(reg_info, reg_value)) | |
109 return reg_value.GetAsUInt64(fail_value, success_ptr); | |
110 if (success_ptr) | |
111 *success_ptr = false; | |
112 return fail_value; | |
113 } | |
114 | |
115 bool EmulateInstruction::WriteRegister(const Context &context, | |
116 const RegisterInfo *reg_info, | |
117 const RegisterValue ®_value) { | |
118 if (m_write_reg_callback != nullptr) | |
119 return m_write_reg_callback(this, m_baton, context, reg_info, reg_value); | |
120 return false; | |
121 } | |
122 | |
123 bool EmulateInstruction::WriteRegister(const Context &context, | |
124 lldb::RegisterKind reg_kind, | |
125 uint32_t reg_num, | |
126 const RegisterValue ®_value) { | |
127 RegisterInfo reg_info; | |
128 if (GetRegisterInfo(reg_kind, reg_num, reg_info)) | |
129 return WriteRegister(context, ®_info, reg_value); | |
130 return false; | |
131 } | |
132 | |
133 bool EmulateInstruction::WriteRegisterUnsigned(const Context &context, | |
134 lldb::RegisterKind reg_kind, | |
135 uint32_t reg_num, | |
136 uint64_t uint_value) { | |
137 RegisterInfo reg_info; | |
138 if (GetRegisterInfo(reg_kind, reg_num, reg_info)) { | |
139 RegisterValue reg_value; | |
140 if (reg_value.SetUInt(uint_value, reg_info.byte_size)) | |
141 return WriteRegister(context, ®_info, reg_value); | |
142 } | |
143 return false; | |
144 } | |
145 | |
146 bool EmulateInstruction::WriteRegisterUnsigned(const Context &context, | |
147 const RegisterInfo *reg_info, | |
148 uint64_t uint_value) { | |
149 if (reg_info != nullptr) { | |
150 RegisterValue reg_value; | |
151 if (reg_value.SetUInt(uint_value, reg_info->byte_size)) | |
152 return WriteRegister(context, reg_info, reg_value); | |
153 } | |
154 return false; | |
155 } | |
156 | |
157 size_t EmulateInstruction::ReadMemory(const Context &context, lldb::addr_t addr, | |
158 void *dst, size_t dst_len) { | |
159 if (m_read_mem_callback != nullptr) | |
160 return m_read_mem_callback(this, m_baton, context, addr, dst, dst_len) == | |
161 dst_len; | |
162 return false; | |
163 } | |
164 | |
165 uint64_t EmulateInstruction::ReadMemoryUnsigned(const Context &context, | |
166 lldb::addr_t addr, | |
167 size_t byte_size, | |
168 uint64_t fail_value, | |
169 bool *success_ptr) { | |
170 uint64_t uval64 = 0; | |
171 bool success = false; | |
172 if (byte_size <= 8) { | |
173 uint8_t buf[sizeof(uint64_t)]; | |
174 size_t bytes_read = | |
175 m_read_mem_callback(this, m_baton, context, addr, buf, byte_size); | |
176 if (bytes_read == byte_size) { | |
177 lldb::offset_t offset = 0; | |
178 DataExtractor data(buf, byte_size, GetByteOrder(), GetAddressByteSize()); | |
179 uval64 = data.GetMaxU64(&offset, byte_size); | |
180 success = true; | |
181 } | |
182 } | |
183 | |
184 if (success_ptr) | |
185 *success_ptr = success; | |
186 | |
187 if (!success) | |
188 uval64 = fail_value; | |
189 return uval64; | |
190 } | |
191 | |
192 bool EmulateInstruction::WriteMemoryUnsigned(const Context &context, | |
193 lldb::addr_t addr, uint64_t uval, | |
194 size_t uval_byte_size) { | |
195 StreamString strm(Stream::eBinary, GetAddressByteSize(), GetByteOrder()); | |
196 strm.PutMaxHex64(uval, uval_byte_size); | |
197 | |
198 size_t bytes_written = m_write_mem_callback( | |
199 this, m_baton, context, addr, strm.GetString().data(), uval_byte_size); | |
200 return (bytes_written == uval_byte_size); | |
201 } | |
202 | |
203 bool EmulateInstruction::WriteMemory(const Context &context, lldb::addr_t addr, | |
204 const void *src, size_t src_len) { | |
205 if (m_write_mem_callback != nullptr) | |
206 return m_write_mem_callback(this, m_baton, context, addr, src, src_len) == | |
207 src_len; | |
208 return false; | |
209 } | |
210 | |
211 void EmulateInstruction::SetBaton(void *baton) { m_baton = baton; } | |
212 | |
213 void EmulateInstruction::SetCallbacks( | |
214 ReadMemoryCallback read_mem_callback, | |
215 WriteMemoryCallback write_mem_callback, | |
216 ReadRegisterCallback read_reg_callback, | |
217 WriteRegisterCallback write_reg_callback) { | |
218 m_read_mem_callback = read_mem_callback; | |
219 m_write_mem_callback = write_mem_callback; | |
220 m_read_reg_callback = read_reg_callback; | |
221 m_write_reg_callback = write_reg_callback; | |
222 } | |
223 | |
224 void EmulateInstruction::SetReadMemCallback( | |
225 ReadMemoryCallback read_mem_callback) { | |
226 m_read_mem_callback = read_mem_callback; | |
227 } | |
228 | |
229 void EmulateInstruction::SetWriteMemCallback( | |
230 WriteMemoryCallback write_mem_callback) { | |
231 m_write_mem_callback = write_mem_callback; | |
232 } | |
233 | |
234 void EmulateInstruction::SetReadRegCallback( | |
235 ReadRegisterCallback read_reg_callback) { | |
236 m_read_reg_callback = read_reg_callback; | |
237 } | |
238 | |
239 void EmulateInstruction::SetWriteRegCallback( | |
240 WriteRegisterCallback write_reg_callback) { | |
241 m_write_reg_callback = write_reg_callback; | |
242 } | |
243 | |
244 // | |
245 // Read & Write Memory and Registers callback functions. | |
246 // | |
247 | |
248 size_t EmulateInstruction::ReadMemoryFrame(EmulateInstruction *instruction, | |
249 void *baton, const Context &context, | |
250 lldb::addr_t addr, void *dst, | |
251 size_t dst_len) { | |
252 if (baton == nullptr || dst == nullptr || dst_len == 0) | |
253 return 0; | |
254 | |
255 StackFrame *frame = (StackFrame *)baton; | |
256 | |
257 ProcessSP process_sp(frame->CalculateProcess()); | |
258 if (process_sp) { | |
259 Status error; | |
260 return process_sp->ReadMemory(addr, dst, dst_len, error); | |
261 } | |
262 return 0; | |
263 } | |
264 | |
265 size_t EmulateInstruction::WriteMemoryFrame(EmulateInstruction *instruction, | |
266 void *baton, const Context &context, | |
267 lldb::addr_t addr, const void *src, | |
268 size_t src_len) { | |
269 if (baton == nullptr || src == nullptr || src_len == 0) | |
270 return 0; | |
271 | |
272 StackFrame *frame = (StackFrame *)baton; | |
273 | |
274 ProcessSP process_sp(frame->CalculateProcess()); | |
275 if (process_sp) { | |
276 Status error; | |
277 return process_sp->WriteMemory(addr, src, src_len, error); | |
278 } | |
279 | |
280 return 0; | |
281 } | |
282 | |
283 bool EmulateInstruction::ReadRegisterFrame(EmulateInstruction *instruction, | |
284 void *baton, | |
285 const RegisterInfo *reg_info, | |
286 RegisterValue ®_value) { | |
287 if (baton == nullptr) | |
288 return false; | |
289 | |
290 StackFrame *frame = (StackFrame *)baton; | |
291 return frame->GetRegisterContext()->ReadRegister(reg_info, reg_value); | |
292 } | |
293 | |
294 bool EmulateInstruction::WriteRegisterFrame(EmulateInstruction *instruction, | |
295 void *baton, const Context &context, | |
296 const RegisterInfo *reg_info, | |
297 const RegisterValue ®_value) { | |
298 if (baton == nullptr) | |
299 return false; | |
300 | |
301 StackFrame *frame = (StackFrame *)baton; | |
302 return frame->GetRegisterContext()->WriteRegister(reg_info, reg_value); | |
303 } | |
304 | |
305 size_t EmulateInstruction::ReadMemoryDefault(EmulateInstruction *instruction, | |
306 void *baton, | |
307 const Context &context, | |
308 lldb::addr_t addr, void *dst, | |
309 size_t length) { | |
310 StreamFile strm(stdout, false); | |
311 strm.Printf(" Read from Memory (address = 0x%" PRIx64 ", length = %" PRIu64 | |
312 ", context = ", | |
313 addr, (uint64_t)length); | |
314 context.Dump(strm, instruction); | |
315 strm.EOL(); | |
316 *((uint64_t *)dst) = 0xdeadbeef; | |
317 return length; | |
318 } | |
319 | |
320 size_t EmulateInstruction::WriteMemoryDefault(EmulateInstruction *instruction, | |
321 void *baton, | |
322 const Context &context, | |
323 lldb::addr_t addr, | |
324 const void *dst, size_t length) { | |
325 StreamFile strm(stdout, false); | |
326 strm.Printf(" Write to Memory (address = 0x%" PRIx64 ", length = %" PRIu64 | |
327 ", context = ", | |
328 addr, (uint64_t)length); | |
329 context.Dump(strm, instruction); | |
330 strm.EOL(); | |
331 return length; | |
332 } | |
333 | |
334 bool EmulateInstruction::ReadRegisterDefault(EmulateInstruction *instruction, | |
335 void *baton, | |
336 const RegisterInfo *reg_info, | |
337 RegisterValue ®_value) { | |
338 StreamFile strm(stdout, false); | |
339 strm.Printf(" Read Register (%s)\n", reg_info->name); | |
340 lldb::RegisterKind reg_kind; | |
341 uint32_t reg_num; | |
342 if (GetBestRegisterKindAndNumber(reg_info, reg_kind, reg_num)) | |
343 reg_value.SetUInt64((uint64_t)reg_kind << 24 | reg_num); | |
344 else | |
345 reg_value.SetUInt64(0); | |
346 | |
347 return true; | |
348 } | |
349 | |
350 bool EmulateInstruction::WriteRegisterDefault(EmulateInstruction *instruction, | |
351 void *baton, | |
352 const Context &context, | |
353 const RegisterInfo *reg_info, | |
354 const RegisterValue ®_value) { | |
355 StreamFile strm(stdout, false); | |
356 strm.Printf(" Write to Register (name = %s, value = ", reg_info->name); | |
357 DumpRegisterValue(reg_value, &strm, reg_info, false, false, eFormatDefault); | |
358 strm.PutCString(", context = "); | |
359 context.Dump(strm, instruction); | |
360 strm.EOL(); | |
361 return true; | |
362 } | |
363 | |
364 void EmulateInstruction::Context::Dump(Stream &strm, | |
365 EmulateInstruction *instruction) const { | |
366 switch (type) { | |
367 case eContextReadOpcode: | |
368 strm.PutCString("reading opcode"); | |
369 break; | |
370 | |
371 case eContextImmediate: | |
372 strm.PutCString("immediate"); | |
373 break; | |
374 | |
375 case eContextPushRegisterOnStack: | |
376 strm.PutCString("push register"); | |
377 break; | |
378 | |
379 case eContextPopRegisterOffStack: | |
380 strm.PutCString("pop register"); | |
381 break; | |
382 | |
383 case eContextAdjustStackPointer: | |
384 strm.PutCString("adjust sp"); | |
385 break; | |
386 | |
387 case eContextSetFramePointer: | |
388 strm.PutCString("set frame pointer"); | |
389 break; | |
390 | |
391 case eContextAdjustBaseRegister: | |
392 strm.PutCString("adjusting (writing value back to) a base register"); | |
393 break; | |
394 | |
395 case eContextRegisterPlusOffset: | |
396 strm.PutCString("register + offset"); | |
397 break; | |
398 | |
399 case eContextRegisterStore: | |
400 strm.PutCString("store register"); | |
401 break; | |
402 | |
403 case eContextRegisterLoad: | |
404 strm.PutCString("load register"); | |
405 break; | |
406 | |
407 case eContextRelativeBranchImmediate: | |
408 strm.PutCString("relative branch immediate"); | |
409 break; | |
410 | |
411 case eContextAbsoluteBranchRegister: | |
412 strm.PutCString("absolute branch register"); | |
413 break; | |
414 | |
415 case eContextSupervisorCall: | |
416 strm.PutCString("supervisor call"); | |
417 break; | |
418 | |
419 case eContextTableBranchReadMemory: | |
420 strm.PutCString("table branch read memory"); | |
421 break; | |
422 | |
423 case eContextWriteRegisterRandomBits: | |
424 strm.PutCString("write random bits to a register"); | |
425 break; | |
426 | |
427 case eContextWriteMemoryRandomBits: | |
428 strm.PutCString("write random bits to a memory address"); | |
429 break; | |
430 | |
431 case eContextArithmetic: | |
432 strm.PutCString("arithmetic"); | |
433 break; | |
434 | |
435 case eContextReturnFromException: | |
436 strm.PutCString("return from exception"); | |
437 break; | |
438 | |
439 default: | |
440 strm.PutCString("unrecognized context."); | |
441 break; | |
442 } | |
443 | |
444 switch (info_type) { | |
445 case eInfoTypeRegisterPlusOffset: | |
446 strm.Printf(" (reg_plus_offset = %s%+" PRId64 ")", | |
447 info.RegisterPlusOffset.reg.name, | |
448 info.RegisterPlusOffset.signed_offset); | |
449 break; | |
450 | |
451 case eInfoTypeRegisterPlusIndirectOffset: | |
452 strm.Printf(" (reg_plus_reg = %s + %s)", | |
453 info.RegisterPlusIndirectOffset.base_reg.name, | |
454 info.RegisterPlusIndirectOffset.offset_reg.name); | |
455 break; | |
456 | |
457 case eInfoTypeRegisterToRegisterPlusOffset: | |
458 strm.Printf(" (base_and_imm_offset = %s%+" PRId64 ", data_reg = %s)", | |
459 info.RegisterToRegisterPlusOffset.base_reg.name, | |
460 info.RegisterToRegisterPlusOffset.offset, | |
461 info.RegisterToRegisterPlusOffset.data_reg.name); | |
462 break; | |
463 | |
464 case eInfoTypeRegisterToRegisterPlusIndirectOffset: | |
465 strm.Printf(" (base_and_reg_offset = %s + %s, data_reg = %s)", | |
466 info.RegisterToRegisterPlusIndirectOffset.base_reg.name, | |
467 info.RegisterToRegisterPlusIndirectOffset.offset_reg.name, | |
468 info.RegisterToRegisterPlusIndirectOffset.data_reg.name); | |
469 break; | |
470 | |
471 case eInfoTypeRegisterRegisterOperands: | |
472 strm.Printf(" (register to register binary op: %s and %s)", | |
473 info.RegisterRegisterOperands.operand1.name, | |
474 info.RegisterRegisterOperands.operand2.name); | |
475 break; | |
476 | |
477 case eInfoTypeOffset: | |
478 strm.Printf(" (signed_offset = %+" PRId64 ")", info.signed_offset); | |
479 break; | |
480 | |
481 case eInfoTypeRegister: | |
482 strm.Printf(" (reg = %s)", info.reg.name); | |
483 break; | |
484 | |
485 case eInfoTypeImmediate: | |
486 strm.Printf(" (unsigned_immediate = %" PRIu64 " (0x%16.16" PRIx64 "))", | |
487 info.unsigned_immediate, info.unsigned_immediate); | |
488 break; | |
489 | |
490 case eInfoTypeImmediateSigned: | |
491 strm.Printf(" (signed_immediate = %+" PRId64 " (0x%16.16" PRIx64 "))", | |
492 info.signed_immediate, info.signed_immediate); | |
493 break; | |
494 | |
495 case eInfoTypeAddress: | |
496 strm.Printf(" (address = 0x%" PRIx64 ")", info.address); | |
497 break; | |
498 | |
499 case eInfoTypeISAAndImmediate: | |
500 strm.Printf(" (isa = %u, unsigned_immediate = %u (0x%8.8x))", | |
501 info.ISAAndImmediate.isa, info.ISAAndImmediate.unsigned_data32, | |
502 info.ISAAndImmediate.unsigned_data32); | |
503 break; | |
504 | |
505 case eInfoTypeISAAndImmediateSigned: | |
506 strm.Printf(" (isa = %u, signed_immediate = %i (0x%8.8x))", | |
507 info.ISAAndImmediateSigned.isa, | |
508 info.ISAAndImmediateSigned.signed_data32, | |
509 info.ISAAndImmediateSigned.signed_data32); | |
510 break; | |
511 | |
512 case eInfoTypeISA: | |
513 strm.Printf(" (isa = %u)", info.isa); | |
514 break; | |
515 | |
516 case eInfoTypeNoArgs: | |
517 break; | |
518 } | |
519 } | |
520 | |
521 bool EmulateInstruction::SetInstruction(const Opcode &opcode, | |
522 const Address &inst_addr, | |
523 Target *target) { | |
524 m_opcode = opcode; | |
525 m_addr = LLDB_INVALID_ADDRESS; | |
526 if (inst_addr.IsValid()) { | |
527 if (target != nullptr) | |
528 m_addr = inst_addr.GetLoadAddress(target); | |
529 if (m_addr == LLDB_INVALID_ADDRESS) | |
530 m_addr = inst_addr.GetFileAddress(); | |
531 } | |
532 return true; | |
533 } | |
534 | |
535 bool EmulateInstruction::GetBestRegisterKindAndNumber( | |
536 const RegisterInfo *reg_info, lldb::RegisterKind ®_kind, | |
537 uint32_t ®_num) { | |
538 // Generic and DWARF should be the two most popular register kinds when | |
539 // emulating instructions since they are the most platform agnostic... | |
540 reg_num = reg_info->kinds[eRegisterKindGeneric]; | |
541 if (reg_num != LLDB_INVALID_REGNUM) { | |
542 reg_kind = eRegisterKindGeneric; | |
543 return true; | |
544 } | |
545 | |
546 reg_num = reg_info->kinds[eRegisterKindDWARF]; | |
547 if (reg_num != LLDB_INVALID_REGNUM) { | |
548 reg_kind = eRegisterKindDWARF; | |
549 return true; | |
550 } | |
551 | |
552 reg_num = reg_info->kinds[eRegisterKindLLDB]; | |
553 if (reg_num != LLDB_INVALID_REGNUM) { | |
554 reg_kind = eRegisterKindLLDB; | |
555 return true; | |
556 } | |
557 | |
558 reg_num = reg_info->kinds[eRegisterKindEHFrame]; | |
559 if (reg_num != LLDB_INVALID_REGNUM) { | |
560 reg_kind = eRegisterKindEHFrame; | |
561 return true; | |
562 } | |
563 | |
564 reg_num = reg_info->kinds[eRegisterKindProcessPlugin]; | |
565 if (reg_num != LLDB_INVALID_REGNUM) { | |
566 reg_kind = eRegisterKindProcessPlugin; | |
567 return true; | |
568 } | |
569 return false; | |
570 } | |
571 | |
572 uint32_t | |
573 EmulateInstruction::GetInternalRegisterNumber(RegisterContext *reg_ctx, | |
574 const RegisterInfo ®_info) { | |
575 lldb::RegisterKind reg_kind; | |
576 uint32_t reg_num; | |
577 if (reg_ctx && GetBestRegisterKindAndNumber(®_info, reg_kind, reg_num)) | |
578 return reg_ctx->ConvertRegisterKindToRegisterNumber(reg_kind, reg_num); | |
579 return LLDB_INVALID_REGNUM; | |
580 } | |
581 | |
582 bool EmulateInstruction::CreateFunctionEntryUnwind(UnwindPlan &unwind_plan) { | |
583 unwind_plan.Clear(); | |
584 return false; | |
585 } |