1b1994897Sopenharmony_ci#!/usr/bin/env ruby 2b1994897Sopenharmony_ci# Copyright (c) 2021-2022 Huawei Device Co., Ltd. 3b1994897Sopenharmony_ci# Licensed under the Apache License, Version 2.0 (the "License"); 4b1994897Sopenharmony_ci# you may not use this file except in compliance with the License. 5b1994897Sopenharmony_ci# You may obtain a copy of the License at 6b1994897Sopenharmony_ci# 7b1994897Sopenharmony_ci# http://www.apache.org/licenses/LICENSE-2.0 8b1994897Sopenharmony_ci# 9b1994897Sopenharmony_ci# Unless required by applicable law or agreed to in writing, software 10b1994897Sopenharmony_ci# distributed under the License is distributed on an "AS IS" BASIS, 11b1994897Sopenharmony_ci# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12b1994897Sopenharmony_ci# See the License for the specific language governing permissions and 13b1994897Sopenharmony_ci# limitations under the License. 14b1994897Sopenharmony_ci 15b1994897Sopenharmony_cirequire 'optparse' 16b1994897Sopenharmony_cirequire 'ostruct' 17b1994897Sopenharmony_cirequire 'logger' 18b1994897Sopenharmony_cirequire 'fileutils' 19b1994897Sopenharmony_cirequire 'open3' 20b1994897Sopenharmony_ci 21b1994897Sopenharmony_cioptions = OpenStruct.new 22b1994897Sopenharmony_ciOptionParser.new do |opts| 23b1994897Sopenharmony_ci opts.banner = 'Usage: checker.rb [options] TEST_FILE' 24b1994897Sopenharmony_ci 25b1994897Sopenharmony_ci opts.on('--run-prefix=PREFIX', 'Prefix that will be inserted before panda run command') do |v| 26b1994897Sopenharmony_ci options.run_prefix = v 27b1994897Sopenharmony_ci end 28b1994897Sopenharmony_ci opts.on('--source=FILE', 'Path to source file') 29b1994897Sopenharmony_ci opts.on('--test-file=FILE', 'Path to test file') do |v| 30b1994897Sopenharmony_ci options.test_file = v 31b1994897Sopenharmony_ci end 32b1994897Sopenharmony_ci opts.on('--panda=PANDA', 'Path to panda') 33b1994897Sopenharmony_ci opts.on('--paoc=PAOC', 'Path to paoc') do |v| 34b1994897Sopenharmony_ci options.paoc = v 35b1994897Sopenharmony_ci end 36b1994897Sopenharmony_ci opts.on('--panda-options=OPTIONS', 'Default options for panda run') do |v| 37b1994897Sopenharmony_ci options.panda_options = v 38b1994897Sopenharmony_ci end 39b1994897Sopenharmony_ci opts.on('--paoc-options=OPTIONS', 'Default options for paoc run') do |v| 40b1994897Sopenharmony_ci options.paoc_options = v 41b1994897Sopenharmony_ci end 42b1994897Sopenharmony_ci opts.on('--command-token=STRING', 'String that is recognized as command start') do |v| 43b1994897Sopenharmony_ci options.command_token = v 44b1994897Sopenharmony_ci end 45b1994897Sopenharmony_ci opts.on('--release', 'Run in release mode. EVENT, INST and other will not be checked') 46b1994897Sopenharmony_ci opts.on('-v', '--verbose', 'Verbose logging') 47b1994897Sopenharmony_ci opts.on('--arch=ARCHITECTURE', 'Architecture of system where start panda') 48b1994897Sopenharmony_ci opts.on("--keep-data", "Do not remove generated data from disk") { |v| options.keep_data = true } 49b1994897Sopenharmony_ciend.parse!(into: options) 50b1994897Sopenharmony_ci 51b1994897Sopenharmony_ci$LOG_LEVEL = options.verbose ? Logger::DEBUG : Logger::ERROR 52b1994897Sopenharmony_ci$curr_cmd = nil 53b1994897Sopenharmony_ci 54b1994897Sopenharmony_cidef log 55b1994897Sopenharmony_ci @log ||= Logger.new($stdout, level: $LOG_LEVEL) 56b1994897Sopenharmony_ciend 57b1994897Sopenharmony_ci 58b1994897Sopenharmony_cidef raise_error(msg) 59b1994897Sopenharmony_ci log.error "Test failed: #{@name}" 60b1994897Sopenharmony_ci log.error msg 61b1994897Sopenharmony_ci log.error "Command to reproduce: #{$curr_cmd}" 62b1994897Sopenharmony_ci raise msg 63b1994897Sopenharmony_ciend 64b1994897Sopenharmony_ci 65b1994897Sopenharmony_cidef match_str(match) 66b1994897Sopenharmony_ci match.is_a?(Regexp) ? "/#{match.source}/" : match 67b1994897Sopenharmony_ciend 68b1994897Sopenharmony_ci 69b1994897Sopenharmony_cidef contains?(str, match) 70b1994897Sopenharmony_ci return str =~ match if match.is_a? Regexp 71b1994897Sopenharmony_ci 72b1994897Sopenharmony_ci raise_error "Wrong type for search: #{match.class}" unless match.is_a? String 73b1994897Sopenharmony_ci str.include? match 74b1994897Sopenharmony_ciend 75b1994897Sopenharmony_ci 76b1994897Sopenharmony_ci# Provides methods to search lines in a given array 77b1994897Sopenharmony_ciclass SearchScope 78b1994897Sopenharmony_ci 79b1994897Sopenharmony_ci attr_reader :lines 80b1994897Sopenharmony_ci 81b1994897Sopenharmony_ci def initialize(lines, name) 82b1994897Sopenharmony_ci @lines = lines 83b1994897Sopenharmony_ci @name = name 84b1994897Sopenharmony_ci @current_index = 0 85b1994897Sopenharmony_ci end 86b1994897Sopenharmony_ci 87b1994897Sopenharmony_ci def self.from_file(fname, name) 88b1994897Sopenharmony_ci SearchScope.new(File.readlines(fname), name) 89b1994897Sopenharmony_ci end 90b1994897Sopenharmony_ci 91b1994897Sopenharmony_ci def find(match) 92b1994897Sopenharmony_ci return if match.nil? 93b1994897Sopenharmony_ci 94b1994897Sopenharmony_ci @current_index = @lines.index { |line| contains?(line, match) } 95b1994897Sopenharmony_ci raise_error "#{@name} not found: #{match_str(match)}" if @current_index.nil? 96b1994897Sopenharmony_ci @current_index += 1 97b1994897Sopenharmony_ci end 98b1994897Sopenharmony_ci 99b1994897Sopenharmony_ci def find_next(match) 100b1994897Sopenharmony_ci return if match.nil? 101b1994897Sopenharmony_ci 102b1994897Sopenharmony_ci index = @lines.drop(@current_index).index { |line| contains?(line, match) } 103b1994897Sopenharmony_ci raise_error "#{@name} not found: #{match_str(match)}" if index.nil? 104b1994897Sopenharmony_ci @current_index += index + 1 105b1994897Sopenharmony_ci end 106b1994897Sopenharmony_ci 107b1994897Sopenharmony_ci def find_not(match) 108b1994897Sopenharmony_ci return if match.nil? 109b1994897Sopenharmony_ci 110b1994897Sopenharmony_ci @lines.each do |line| 111b1994897Sopenharmony_ci raise_error "#{@name} should not occur: #{match_str(match)}" if contains?(line, match) 112b1994897Sopenharmony_ci end 113b1994897Sopenharmony_ci end 114b1994897Sopenharmony_ci 115b1994897Sopenharmony_ci def find_next_not(match) 116b1994897Sopenharmony_ci return if match.nil? 117b1994897Sopenharmony_ci 118b1994897Sopenharmony_ci @lines.drop(@current_index).each do |line| 119b1994897Sopenharmony_ci raise_error "#{@name} should not occur: #{match_str(match)}" if contains?(line, match) 120b1994897Sopenharmony_ci end 121b1994897Sopenharmony_ci end 122b1994897Sopenharmony_ci 123b1994897Sopenharmony_ci def to_s 124b1994897Sopenharmony_ci "Scope '#{@name}', current=#{@current_index}\n#{@lines.join}" 125b1994897Sopenharmony_ci end 126b1994897Sopenharmony_ciend 127b1994897Sopenharmony_ci 128b1994897Sopenharmony_ciclass Checker 129b1994897Sopenharmony_ci attr_reader :name 130b1994897Sopenharmony_ci 131b1994897Sopenharmony_ci def initialize(options, name) 132b1994897Sopenharmony_ci @name = name 133b1994897Sopenharmony_ci @lines = [] 134b1994897Sopenharmony_ci @code = "" 135b1994897Sopenharmony_ci @cwd = "#{Dir.getwd}/#{name.gsub(/[ -:()]/, '_')}" 136b1994897Sopenharmony_ci @options = options 137b1994897Sopenharmony_ci @args = '' 138b1994897Sopenharmony_ci @ir_files = [] 139b1994897Sopenharmony_ci @architecture = options.arch 140b1994897Sopenharmony_ci @aot_file = '' 141b1994897Sopenharmony_ci 142b1994897Sopenharmony_ci # Events scope for 'events.csv' 143b1994897Sopenharmony_ci @events_scope = nil 144b1994897Sopenharmony_ci # IR scope for IR dumps files 'ir_dump/*.ir' 145b1994897Sopenharmony_ci @ir_scope = nil 146b1994897Sopenharmony_ci 147b1994897Sopenharmony_ci # Disassembly file lines, that were read from 'disasm.txt' 148b1994897Sopenharmony_ci @disasm_lines = nil 149b1994897Sopenharmony_ci # Currently processing disasm method 150b1994897Sopenharmony_ci @disasm_method_scope = nil 151b1994897Sopenharmony_ci # Current search scope 152b1994897Sopenharmony_ci @disasm_scope = nil 153b1994897Sopenharmony_ci 154b1994897Sopenharmony_ci Dir.mkdir(@cwd) unless File.exists?(@cwd) 155b1994897Sopenharmony_ci clear_data 156b1994897Sopenharmony_ci end 157b1994897Sopenharmony_ci 158b1994897Sopenharmony_ci def append_line(line) 159b1994897Sopenharmony_ci @code << line 160b1994897Sopenharmony_ci end 161b1994897Sopenharmony_ci 162b1994897Sopenharmony_ci def RUN(**args) 163b1994897Sopenharmony_ci expected_result = 0 164b1994897Sopenharmony_ci aborted_sig = 0 165b1994897Sopenharmony_ci entry = '_GLOBAL::main' 166b1994897Sopenharmony_ci env = '' 167b1994897Sopenharmony_ci args.each do |name, value| 168b1994897Sopenharmony_ci if name == :force_jit and value 169b1994897Sopenharmony_ci @args << '--compiler-hotness-threshold=0 --no-async-jit=true --compiler-enable-jit=true ' 170b1994897Sopenharmony_ci elsif name == :options 171b1994897Sopenharmony_ci @args << value 172b1994897Sopenharmony_ci elsif name == :entry 173b1994897Sopenharmony_ci entry = value 174b1994897Sopenharmony_ci elsif name == :result 175b1994897Sopenharmony_ci expected_result = value 176b1994897Sopenharmony_ci elsif name == :abort 177b1994897Sopenharmony_ci aborted_sig = value 178b1994897Sopenharmony_ci elsif name == :env 179b1994897Sopenharmony_ci env = value 180b1994897Sopenharmony_ci end 181b1994897Sopenharmony_ci end 182b1994897Sopenharmony_ci 183b1994897Sopenharmony_ci clear_data 184b1994897Sopenharmony_ci aot_arg = @aot_file.empty? ? '' : "--aot-file #{@aot_file}" 185b1994897Sopenharmony_ci 186b1994897Sopenharmony_ci $curr_cmd = "#{env} #{@options.run_prefix} #{@options.panda} --compiler-ignore-failures=false #{@options.panda_options} \ 187b1994897Sopenharmony_ci #{aot_arg} #{@args} --events-output=csv --compiler-dump --compiler-disasm-dump:single-file #{@options.test_file} #{entry}" 188b1994897Sopenharmony_ci log.debug "Panda command: #{$curr_cmd}" 189b1994897Sopenharmony_ci 190b1994897Sopenharmony_ci output, status = Open3.capture2e($curr_cmd.to_s, chdir: @cwd.to_s) 191b1994897Sopenharmony_ci if status.signaled? 192b1994897Sopenharmony_ci if status.termsig != aborted_sig 193b1994897Sopenharmony_ci puts output 194b1994897Sopenharmony_ci log.error "panda aborted with signal #{status.termsig}, but expected #{aborted_sig}" 195b1994897Sopenharmony_ci raise_error "Test '#{@name}' failed" 196b1994897Sopenharmony_ci end 197b1994897Sopenharmony_ci elsif status.exitstatus != expected_result 198b1994897Sopenharmony_ci puts output 199b1994897Sopenharmony_ci log.error "panda returns code #{status.exitstatus}, but expected #{expected_result}" 200b1994897Sopenharmony_ci raise_error "Test '#{@name}' failed" 201b1994897Sopenharmony_ci end 202b1994897Sopenharmony_ci log.debug output 203b1994897Sopenharmony_ci 204b1994897Sopenharmony_ci @events_scope = SearchScope.from_file("#{@cwd}/events.csv", 'Events') 205b1994897Sopenharmony_ci @ir_files = Dir['ir_dump/*.ir'] 206b1994897Sopenharmony_ci end 207b1994897Sopenharmony_ci 208b1994897Sopenharmony_ci def RUN_PAOC(**args) 209b1994897Sopenharmony_ci @aot_file = "#{Dir.getwd}/#{File.basename(@options.test_file, File.extname(@options.test_file))}.an" 210b1994897Sopenharmony_ci 211b1994897Sopenharmony_ci inputs = @options.test_file 212b1994897Sopenharmony_ci aot_output_option = '--paoc-output' 213b1994897Sopenharmony_ci output = @aot_file 214b1994897Sopenharmony_ci options = '' 215b1994897Sopenharmony_ci env = '' 216b1994897Sopenharmony_ci aborted_sig = 0 217b1994897Sopenharmony_ci 218b1994897Sopenharmony_ci args.each do |name, value| 219b1994897Sopenharmony_ci case name 220b1994897Sopenharmony_ci when :options 221b1994897Sopenharmony_ci options = value 222b1994897Sopenharmony_ci when :boot 223b1994897Sopenharmony_ci aot_output_option = '--paoc-boot-output' 224b1994897Sopenharmony_ci when :env 225b1994897Sopenharmony_ci env = value 226b1994897Sopenharmony_ci when :inputs 227b1994897Sopenharmony_ci inputs = value 228b1994897Sopenharmony_ci when :abort 229b1994897Sopenharmony_ci aborted_sig = value 230b1994897Sopenharmony_ci when :output 231b1994897Sopenharmony_ci output = value 232b1994897Sopenharmony_ci end 233b1994897Sopenharmony_ci end 234b1994897Sopenharmony_ci 235b1994897Sopenharmony_ci paoc_args = "--paoc-panda-files #{inputs} --events-output=csv --compiler-dump #{options} #{aot_output_option} #{output}" 236b1994897Sopenharmony_ci 237b1994897Sopenharmony_ci clear_data 238b1994897Sopenharmony_ci 239b1994897Sopenharmony_ci $curr_cmd = "#{env} #{@options.run_prefix} #{@options.paoc} --compiler-ignore-failures=false --compiler-disasm-dump:single-file #{@options.paoc_options} #{paoc_args}" 240b1994897Sopenharmony_ci log.debug "Paoc command: #{$curr_cmd}" 241b1994897Sopenharmony_ci 242b1994897Sopenharmony_ci output, status = Open3.capture2e($curr_cmd.to_s, chdir: @cwd.to_s) 243b1994897Sopenharmony_ci if status.signaled? 244b1994897Sopenharmony_ci if status.termsig != aborted_sig 245b1994897Sopenharmony_ci puts output 246b1994897Sopenharmony_ci log.error "panda aborted with signal #{status.termsig}, but expected #{aborted_sig}" 247b1994897Sopenharmony_ci raise_error "Test '#{@name}' failed" 248b1994897Sopenharmony_ci end 249b1994897Sopenharmony_ci elsif status.exitstatus != 0 250b1994897Sopenharmony_ci puts output 251b1994897Sopenharmony_ci log.error "paoc failed: #{status.exitstatus}" 252b1994897Sopenharmony_ci raise_error "Test '#{@name}' failed" 253b1994897Sopenharmony_ci end 254b1994897Sopenharmony_ci log.debug output 255b1994897Sopenharmony_ci 256b1994897Sopenharmony_ci @events_scope = SearchScope.from_file("#{@cwd}/events.csv", 'Events') 257b1994897Sopenharmony_ci @ir_files = Dir['ir_dump/*.ir'] 258b1994897Sopenharmony_ci end 259b1994897Sopenharmony_ci 260b1994897Sopenharmony_ci def EVENT(match) 261b1994897Sopenharmony_ci return if @options.release 262b1994897Sopenharmony_ci 263b1994897Sopenharmony_ci @events_scope.find(match) 264b1994897Sopenharmony_ci end 265b1994897Sopenharmony_ci 266b1994897Sopenharmony_ci def EVENT_NEXT(match) 267b1994897Sopenharmony_ci return if @options.release 268b1994897Sopenharmony_ci 269b1994897Sopenharmony_ci @events_scope.find_next(match) 270b1994897Sopenharmony_ci end 271b1994897Sopenharmony_ci 272b1994897Sopenharmony_ci def EVENT_COUNT(match) 273b1994897Sopenharmony_ci return 0 if @options.release 274b1994897Sopenharmony_ci 275b1994897Sopenharmony_ci @events_scope.lines.count { |event| contains?(event, match) } 276b1994897Sopenharmony_ci end 277b1994897Sopenharmony_ci 278b1994897Sopenharmony_ci def EVENT_NOT(match) 279b1994897Sopenharmony_ci return if @options.release 280b1994897Sopenharmony_ci 281b1994897Sopenharmony_ci @events_scope.find_not(match) 282b1994897Sopenharmony_ci end 283b1994897Sopenharmony_ci 284b1994897Sopenharmony_ci def EVENT_NEXT_NOT(match) 285b1994897Sopenharmony_ci return if @options.release 286b1994897Sopenharmony_ci 287b1994897Sopenharmony_ci @events_scope.find_next_not(match) 288b1994897Sopenharmony_ci end 289b1994897Sopenharmony_ci 290b1994897Sopenharmony_ci def EVENTS_COUNT(match, count) 291b1994897Sopenharmony_ci return if @options.release 292b1994897Sopenharmony_ci 293b1994897Sopenharmony_ci res = @events_scope.lines.count { |event| contains?(event, match) } 294b1994897Sopenharmony_ci raise_error "Events count missmatch for #{match}, expected: #{count}, real: #{res}" unless res == count 295b1994897Sopenharmony_ci end 296b1994897Sopenharmony_ci 297b1994897Sopenharmony_ci def TRUE(condition) 298b1994897Sopenharmony_ci return if @options.release 299b1994897Sopenharmony_ci 300b1994897Sopenharmony_ci raise_error "Not true condition: \"#{condition}\"" unless condition 301b1994897Sopenharmony_ci end 302b1994897Sopenharmony_ci 303b1994897Sopenharmony_ci class SkipException < StandardError 304b1994897Sopenharmony_ci end 305b1994897Sopenharmony_ci 306b1994897Sopenharmony_ci def SKIP_IF(condition) 307b1994897Sopenharmony_ci return if @options.release 308b1994897Sopenharmony_ci raise SkipException if condition 309b1994897Sopenharmony_ci end 310b1994897Sopenharmony_ci 311b1994897Sopenharmony_ci def IR_COUNT(match) 312b1994897Sopenharmony_ci return 0 if @options.release 313b1994897Sopenharmony_ci 314b1994897Sopenharmony_ci @ir_scope.lines.count { |inst| contains?(inst, match) } 315b1994897Sopenharmony_ci end 316b1994897Sopenharmony_ci 317b1994897Sopenharmony_ci def BLOCK_COUNT 318b1994897Sopenharmony_ci IR_COUNT('BB ') 319b1994897Sopenharmony_ci end 320b1994897Sopenharmony_ci 321b1994897Sopenharmony_ci def INST(match) 322b1994897Sopenharmony_ci return if @options.release 323b1994897Sopenharmony_ci 324b1994897Sopenharmony_ci @ir_scope.find(match) 325b1994897Sopenharmony_ci end 326b1994897Sopenharmony_ci 327b1994897Sopenharmony_ci def INST_NEXT(match) 328b1994897Sopenharmony_ci return if @options.release 329b1994897Sopenharmony_ci 330b1994897Sopenharmony_ci @ir_scope.find_next(match) 331b1994897Sopenharmony_ci end 332b1994897Sopenharmony_ci 333b1994897Sopenharmony_ci def INST_NOT(match) 334b1994897Sopenharmony_ci return if @options.release 335b1994897Sopenharmony_ci 336b1994897Sopenharmony_ci @ir_scope.find_not(match) 337b1994897Sopenharmony_ci end 338b1994897Sopenharmony_ci 339b1994897Sopenharmony_ci def INST_NEXT_NOT(match) 340b1994897Sopenharmony_ci return if @options.release 341b1994897Sopenharmony_ci 342b1994897Sopenharmony_ci @ir_scope.find_next_not(match) 343b1994897Sopenharmony_ci end 344b1994897Sopenharmony_ci 345b1994897Sopenharmony_ci def INST_COUNT(match, count) 346b1994897Sopenharmony_ci return if @options.release 347b1994897Sopenharmony_ci 348b1994897Sopenharmony_ci real_count = IR_COUNT(match) 349b1994897Sopenharmony_ci raise_error "IR_COUNT mismatch: expected=#{count}, real=#{real_count}" unless real_count == count 350b1994897Sopenharmony_ci end 351b1994897Sopenharmony_ci 352b1994897Sopenharmony_ci module SearchState 353b1994897Sopenharmony_ci NONE = 0 354b1994897Sopenharmony_ci SEARCH_BODY = 1 355b1994897Sopenharmony_ci SEARCH_END = 2 356b1994897Sopenharmony_ci end 357b1994897Sopenharmony_ci 358b1994897Sopenharmony_ci def ASM_METHOD(match) 359b1994897Sopenharmony_ci ensure_disasm 360b1994897Sopenharmony_ci state = SearchState::NONE 361b1994897Sopenharmony_ci start_index = nil 362b1994897Sopenharmony_ci end_index = -1 363b1994897Sopenharmony_ci @disasm_lines.each_with_index do |line, index| 364b1994897Sopenharmony_ci case state 365b1994897Sopenharmony_ci when SearchState::NONE 366b1994897Sopenharmony_ci if line.start_with?('METHOD_INFO:') && contains?(@disasm_lines[index + 1].split(':', 2)[1].strip, match) 367b1994897Sopenharmony_ci state = SearchState::SEARCH_BODY 368b1994897Sopenharmony_ci end 369b1994897Sopenharmony_ci when SearchState::SEARCH_BODY 370b1994897Sopenharmony_ci if line.start_with?('DISASSEMBLY') 371b1994897Sopenharmony_ci start_index = index + 1 372b1994897Sopenharmony_ci state = SearchState::SEARCH_END 373b1994897Sopenharmony_ci end 374b1994897Sopenharmony_ci when SearchState::SEARCH_END 375b1994897Sopenharmony_ci if line.start_with?('METHOD_INFO:') 376b1994897Sopenharmony_ci end_index = index - 1 377b1994897Sopenharmony_ci break 378b1994897Sopenharmony_ci end 379b1994897Sopenharmony_ci end 380b1994897Sopenharmony_ci end 381b1994897Sopenharmony_ci raise "Method not found: #{match_str(match)}" if start_index.nil? 382b1994897Sopenharmony_ci 383b1994897Sopenharmony_ci @disasm_method_scope = SearchScope.new(@disasm_lines[start_index..end_index], "Method: #{match_str(match)}") 384b1994897Sopenharmony_ci @disasm_scope = @disasm_method_scope 385b1994897Sopenharmony_ci end 386b1994897Sopenharmony_ci 387b1994897Sopenharmony_ci def ASM_INST(match) 388b1994897Sopenharmony_ci ensure_disasm 389b1994897Sopenharmony_ci state = SearchState::NONE 390b1994897Sopenharmony_ci start_index = nil 391b1994897Sopenharmony_ci end_index = -1 392b1994897Sopenharmony_ci prefix = nil 393b1994897Sopenharmony_ci @disasm_method_scope.lines.each_with_index do |line, index| 394b1994897Sopenharmony_ci case state 395b1994897Sopenharmony_ci when SearchState::NONE 396b1994897Sopenharmony_ci if contains?(line, match) 397b1994897Sopenharmony_ci prefix = line.sub(/#.*/, '#').gsub("\n", '') 398b1994897Sopenharmony_ci start_index = index + 1 399b1994897Sopenharmony_ci state = SearchState::SEARCH_END 400b1994897Sopenharmony_ci end 401b1994897Sopenharmony_ci when SearchState::SEARCH_END 402b1994897Sopenharmony_ci if line.start_with?(prefix) 403b1994897Sopenharmony_ci end_index = index - 1 404b1994897Sopenharmony_ci break 405b1994897Sopenharmony_ci end 406b1994897Sopenharmony_ci end 407b1994897Sopenharmony_ci end 408b1994897Sopenharmony_ci raise "Can not find asm instruction: #{match}" if start_index.nil? 409b1994897Sopenharmony_ci 410b1994897Sopenharmony_ci @disasm_scope = SearchScope.new(@disasm_method_scope.lines[start_index..end_index], "Inst: #{match_str(match)}") 411b1994897Sopenharmony_ci end 412b1994897Sopenharmony_ci 413b1994897Sopenharmony_ci def ASM_RESET 414b1994897Sopenharmony_ci @disasm_scope = @disasm_method_scope 415b1994897Sopenharmony_ci end 416b1994897Sopenharmony_ci 417b1994897Sopenharmony_ci def ASM(**kwargs) 418b1994897Sopenharmony_ci ensure_disasm 419b1994897Sopenharmony_ci @disasm_scope.find(select_asm(kwargs)) 420b1994897Sopenharmony_ci end 421b1994897Sopenharmony_ci 422b1994897Sopenharmony_ci def ASM_NEXT(**kwargs) 423b1994897Sopenharmony_ci ensure_disasm 424b1994897Sopenharmony_ci @disasm_scope.find_next(select_asm(kwargs)) 425b1994897Sopenharmony_ci end 426b1994897Sopenharmony_ci 427b1994897Sopenharmony_ci def ASM_NOT(**kwargs) 428b1994897Sopenharmony_ci ensure_disasm 429b1994897Sopenharmony_ci @disasm_scope.find_not(select_asm(kwargs)) 430b1994897Sopenharmony_ci end 431b1994897Sopenharmony_ci 432b1994897Sopenharmony_ci def ASM_NEXT_NOT(**kwargs) 433b1994897Sopenharmony_ci ensure_disasm 434b1994897Sopenharmony_ci @disasm_scope.find_next_not(select_asm(kwargs)) 435b1994897Sopenharmony_ci end 436b1994897Sopenharmony_ci 437b1994897Sopenharmony_ci def select_asm(kwargs) 438b1994897Sopenharmony_ci kwargs[@options.arch.to_sym] 439b1994897Sopenharmony_ci end 440b1994897Sopenharmony_ci 441b1994897Sopenharmony_ci def ensure_disasm 442b1994897Sopenharmony_ci @disasm_lines ||= File.readlines("#{@cwd}/disasm.txt") 443b1994897Sopenharmony_ci end 444b1994897Sopenharmony_ci 445b1994897Sopenharmony_ci def METHOD(method) 446b1994897Sopenharmony_ci return if @options.release 447b1994897Sopenharmony_ci 448b1994897Sopenharmony_ci @ir_files = Dir["#{@cwd}/ir_dump/*#{method.sub('::', '_')}*.ir"] 449b1994897Sopenharmony_ci @ir_files.sort! 450b1994897Sopenharmony_ci raise_error "IR dumps not found for method: #{method.sub('::', '_')}" if @ir_files.empty? 451b1994897Sopenharmony_ci @current_method = method 452b1994897Sopenharmony_ci end 453b1994897Sopenharmony_ci 454b1994897Sopenharmony_ci def PASS_AFTER(pass) 455b1994897Sopenharmony_ci return if @options.release 456b1994897Sopenharmony_ci 457b1994897Sopenharmony_ci fname = @ir_files.detect { |x| File.basename(x).include? pass } 458b1994897Sopenharmony_ci raise_error "IR file not found for pass: #{pass}" unless fname 459b1994897Sopenharmony_ci @ir_scope = SearchScope.from_file(fname, 'IR') 460b1994897Sopenharmony_ci end 461b1994897Sopenharmony_ci 462b1994897Sopenharmony_ci def PASS_BEFORE(pass) 463b1994897Sopenharmony_ci return if @options.release 464b1994897Sopenharmony_ci 465b1994897Sopenharmony_ci index = @ir_files.index { |x| File.basename(x).include? pass } 466b1994897Sopenharmony_ci raise_error "IR file not found for pass: #{pass}" unless index 467b1994897Sopenharmony_ci @ir_scope = SearchScope.from_file(@ir_files[index - 1], 'IR') 468b1994897Sopenharmony_ci end 469b1994897Sopenharmony_ci 470b1994897Sopenharmony_ci def run 471b1994897Sopenharmony_ci log.info "Running \"#{@name}\"" 472b1994897Sopenharmony_ci begin 473b1994897Sopenharmony_ci self.instance_eval @code 474b1994897Sopenharmony_ci rescue SkipException 475b1994897Sopenharmony_ci log.info "Skipped: \"#{@name}\"" 476b1994897Sopenharmony_ci else 477b1994897Sopenharmony_ci log.info "Success: \"#{@name}\"" 478b1994897Sopenharmony_ci end 479b1994897Sopenharmony_ci clear_data 480b1994897Sopenharmony_ci end 481b1994897Sopenharmony_ci 482b1994897Sopenharmony_ci def clear_data 483b1994897Sopenharmony_ci if !@options.keep_data 484b1994897Sopenharmony_ci FileUtils.rm_rf("#{@cwd}/ir_dump") 485b1994897Sopenharmony_ci FileUtils.rm_rf("#{@cwd}/events.csv") 486b1994897Sopenharmony_ci FileUtils.rm_rf("#{@cwd}/disasm.txt") 487b1994897Sopenharmony_ci end 488b1994897Sopenharmony_ci end 489b1994897Sopenharmony_ciend 490b1994897Sopenharmony_ci 491b1994897Sopenharmony_cidef read_checks(options) 492b1994897Sopenharmony_ci checks = [] 493b1994897Sopenharmony_ci check = nil 494b1994897Sopenharmony_ci checker_start = "#{options.command_token} CHECKER" 495b1994897Sopenharmony_ci File.readlines(options.source).each do |line| 496b1994897Sopenharmony_ci if check 497b1994897Sopenharmony_ci unless line.start_with? options.command_token 498b1994897Sopenharmony_ci check = nil 499b1994897Sopenharmony_ci next 500b1994897Sopenharmony_ci end 501b1994897Sopenharmony_ci check.append_line(line[options.command_token.size..-1]) 502b1994897Sopenharmony_ci else 503b1994897Sopenharmony_ci next unless line.start_with? checker_start 504b1994897Sopenharmony_ci 505b1994897Sopenharmony_ci name = line.split(' ', 3)[2].strip 506b1994897Sopenharmony_ci raise "Checker with name '#{name}'' already exists" if checks.any? { |x| x.name == name } 507b1994897Sopenharmony_ci 508b1994897Sopenharmony_ci check = Checker.new(options, name) 509b1994897Sopenharmony_ci checks << check 510b1994897Sopenharmony_ci end 511b1994897Sopenharmony_ci end 512b1994897Sopenharmony_ci checks 513b1994897Sopenharmony_ciend 514b1994897Sopenharmony_ci 515b1994897Sopenharmony_cidef main(options) 516b1994897Sopenharmony_ci read_checks(options).each(&:run) 517b1994897Sopenharmony_ci 0 518b1994897Sopenharmony_ciend 519b1994897Sopenharmony_ci 520b1994897Sopenharmony_ciif __FILE__ == $PROGRAM_NAME 521b1994897Sopenharmony_ci main(options) 522b1994897Sopenharmony_ciend 523b1994897Sopenharmony_ci 524b1994897Sopenharmony_ci# Somehow ruby resolves `Checker` name to another class in a Testing scope, so make this global 525b1994897Sopenharmony_ci# variable to refer to it from unit tests. I believe there is more proper way to do it, but I 526b1994897Sopenharmony_ci# didn't find it at first glance. 527b1994897Sopenharmony_ci$CheckerForTest = Checker