postfix_evaluator_unittest.cc revision 4e518a4357a2d1c379d4a91df6d4e153ee791101
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// postfix_evaluator_unittest.cc: Unit tests for PostfixEvaluator. 31// 32// Author: Mark Mentovai 33 34#include <stdio.h> 35 36#include <map> 37#include <string> 38 39#include "processor/postfix_evaluator-inl.h" 40 41#include "common/using_std_string.h" 42#include "google_breakpad/common/breakpad_types.h" 43#include "google_breakpad/processor/memory_region.h" 44#include "processor/logging.h" 45 46 47namespace { 48 49 50using std::map; 51using google_breakpad::MemoryRegion; 52using google_breakpad::PostfixEvaluator; 53 54 55// FakeMemoryRegion is used to test PostfixEvaluator's dereference (^) 56// operator. The result of dereferencing a value is one greater than 57// the value. 58class FakeMemoryRegion : public MemoryRegion { 59 public: 60 virtual u_int64_t GetBase() const { return 0; } 61 virtual u_int32_t GetSize() const { return 0; } 62 virtual bool GetMemoryAtAddress(u_int64_t address, u_int8_t *value) const { 63 *value = address + 1; 64 return true; 65 } 66 virtual bool GetMemoryAtAddress(u_int64_t address, u_int16_t *value) const { 67 *value = address + 1; 68 return true; 69 } 70 virtual bool GetMemoryAtAddress(u_int64_t address, u_int32_t *value) const { 71 *value = address + 1; 72 return true; 73 } 74 virtual bool GetMemoryAtAddress(u_int64_t address, u_int64_t *value) const { 75 *value = address + 1; 76 return true; 77 } 78}; 79 80 81struct EvaluateTest { 82 // Expression passed to PostfixEvaluator::Evaluate. 83 const string expression; 84 85 // True if the expression is expected to be evaluable, false if evaluation 86 // is expected to fail. 87 bool evaluable; 88}; 89 90 91struct EvaluateTestSet { 92 // The dictionary used for all tests in the set. 93 PostfixEvaluator<unsigned int>::DictionaryType *dictionary; 94 95 // The list of tests. 96 const EvaluateTest *evaluate_tests; 97 98 // The number of tests. 99 unsigned int evaluate_test_count; 100 101 // Identifiers and their expected values upon completion of the Evaluate 102 // tests in the set. 103 map<string, unsigned int> *validate_data; 104}; 105 106 107struct EvaluateForValueTest { 108 // Expression passed to PostfixEvaluator::Evaluate. 109 const string expression; 110 111 // True if the expression is expected to be evaluable, false if evaluation 112 // is expected to fail. 113 bool evaluable; 114 115 // If evaluable, the value we expect it to yield. 116 unsigned int value; 117}; 118 119static bool RunTests() { 120 // The first test set checks the basic operations and failure modes. 121 PostfixEvaluator<unsigned int>::DictionaryType dictionary_0; 122 const EvaluateTest evaluate_tests_0[] = { 123 { "$rAdd 2 2 + =", true }, // $rAdd = 2 + 2 = 4 124 { "$rAdd $rAdd 2 + =", true }, // $rAdd = $rAdd + 2 = 6 125 { "$rAdd 2 $rAdd + =", true }, // $rAdd = 2 + $rAdd = 8 126 { "99", false }, // put some junk on the stack... 127 { "$rAdd2 2 2 + =", true }, // ...and make sure things still work 128 { "$rAdd2\t2\n2 + =", true }, // same but with different whitespace 129 { "$rAdd2 2 2 + = ", true }, // trailing whitespace 130 { " $rAdd2 2 2 + =", true }, // leading whitespace 131 { "$rAdd2 2 2 + =", true }, // extra whitespace 132 { "$T0 2 = +", false }, // too few operands for add 133 { "2 + =", false }, // too few operands for add 134 { "2 +", false }, // too few operands for add 135 { "+", false }, // too few operands for add 136 { "^", false }, // too few operands for dereference 137 { "=", false }, // too few operands for assignment 138 { "2 =", false }, // too few operands for assignment 139 { "2 2 + =", false }, // too few operands for assignment 140 { "2 2 =", false }, // can't assign into a literal 141 { "k 2 =", false }, // can't assign into a constant 142 { "2", false }, // leftover data on stack 143 { "2 2 +", false }, // leftover data on stack 144 { "$rAdd", false }, // leftover data on stack 145 { "0 $T1 0 0 + =", false }, // leftover data on stack 146 { "$T2 $T2 2 + =", false }, // can't operate on an undefined value 147 { "$rMul 9 6 * =", true }, // $rMul = 9 * 6 = 54 148 { "$rSub 9 6 - =", true }, // $rSub = 9 - 6 = 3 149 { "$rDivQ 9 6 / =", true }, // $rDivQ = 9 / 6 = 1 150 { "$rDivM 9 6 % =", true }, // $rDivM = 9 % 6 = 3 151 { "$rDeref 9 ^ =", true }, // $rDeref = ^9 = 10 (FakeMemoryRegion) 152 { "$rAlign 36 8 @ =", true }, // $rAlign = 36 @ 8 153 { "$rAdd3 2 2 + =$rMul2 9 6 * =", true } // smashed-equals tokenization 154 }; 155 map<string, unsigned int> validate_data_0; 156 validate_data_0["$rAdd"] = 8; 157 validate_data_0["$rAdd2"] = 4; 158 validate_data_0["$rSub"] = 3; 159 validate_data_0["$rMul"] = 54; 160 validate_data_0["$rDivQ"] = 1; 161 validate_data_0["$rDivM"] = 3; 162 validate_data_0["$rDeref"] = 10; 163 validate_data_0["$rAlign"] = 32; 164 validate_data_0["$rAdd3"] = 4; 165 validate_data_0["$rMul2"] = 54; 166 167 // The second test set simulates a couple of MSVC program strings. 168 // The data is fudged a little bit because the tests use FakeMemoryRegion 169 // instead of a real stack snapshot, but the program strings are real and 170 // the implementation doesn't know or care that the data is not real. 171 PostfixEvaluator<unsigned int>::DictionaryType dictionary_1; 172 dictionary_1["$ebp"] = 0xbfff0010; 173 dictionary_1["$eip"] = 0x10000000; 174 dictionary_1["$esp"] = 0xbfff0000; 175 dictionary_1[".cbSavedRegs"] = 4; 176 dictionary_1[".cbParams"] = 4; 177 dictionary_1[".raSearchStart"] = 0xbfff0020; 178 const EvaluateTest evaluate_tests_1[] = { 179 { "$T0 $ebp = $eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = " 180 "$L $T0 .cbSavedRegs - = $P $T0 8 + .cbParams + =", true }, 181 // Intermediate state: $T0 = 0xbfff0010, $eip = 0xbfff0015, 182 // $ebp = 0xbfff0011, $esp = 0xbfff0018, 183 // $L = 0xbfff000c, $P = 0xbfff001c 184 { "$T0 $ebp = $eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = " 185 "$L $T0 .cbSavedRegs - = $P $T0 8 + .cbParams + = $ebx $T0 28 - ^ =", 186 true }, 187 // Intermediate state: $T0 = 0xbfff0011, $eip = 0xbfff0016, 188 // $ebp = 0xbfff0012, $esp = 0xbfff0019, 189 // $L = 0xbfff000d, $P = 0xbfff001d, 190 // $ebx = 0xbffefff6 191 { "$T0 $ebp = $T2 $esp = $T1 .raSearchStart = $eip $T1 ^ = $ebp $T0 = " 192 "$esp $T1 4 + = $L $T0 .cbSavedRegs - = $P $T1 4 + .cbParams + = " 193 "$ebx $T0 28 - ^ =", 194 true } 195 }; 196 map<string, unsigned int> validate_data_1; 197 validate_data_1["$T0"] = 0xbfff0012; 198 validate_data_1["$T1"] = 0xbfff0020; 199 validate_data_1["$T2"] = 0xbfff0019; 200 validate_data_1["$eip"] = 0xbfff0021; 201 validate_data_1["$ebp"] = 0xbfff0012; 202 validate_data_1["$esp"] = 0xbfff0024; 203 validate_data_1["$L"] = 0xbfff000e; 204 validate_data_1["$P"] = 0xbfff0028; 205 validate_data_1["$ebx"] = 0xbffefff7; 206 validate_data_1[".cbSavedRegs"] = 4; 207 validate_data_1[".cbParams"] = 4; 208 209 EvaluateTestSet evaluate_test_sets[] = { 210 { &dictionary_0, evaluate_tests_0, 211 sizeof(evaluate_tests_0) / sizeof(EvaluateTest), &validate_data_0 }, 212 { &dictionary_1, evaluate_tests_1, 213 sizeof(evaluate_tests_1) / sizeof(EvaluateTest), &validate_data_1 }, 214 }; 215 216 unsigned int evaluate_test_set_count = sizeof(evaluate_test_sets) / 217 sizeof(EvaluateTestSet); 218 219 FakeMemoryRegion fake_memory; 220 PostfixEvaluator<unsigned int> postfix_evaluator = 221 PostfixEvaluator<unsigned int>(NULL, &fake_memory); 222 223 for (unsigned int evaluate_test_set_index = 0; 224 evaluate_test_set_index < evaluate_test_set_count; 225 ++evaluate_test_set_index) { 226 EvaluateTestSet *evaluate_test_set = 227 &evaluate_test_sets[evaluate_test_set_index]; 228 const EvaluateTest *evaluate_tests = evaluate_test_set->evaluate_tests; 229 unsigned int evaluate_test_count = evaluate_test_set->evaluate_test_count; 230 231 // The same dictionary will be used for each test in the set. Earlier 232 // tests can affect the state of the dictionary for later tests. 233 postfix_evaluator.set_dictionary(evaluate_test_set->dictionary); 234 235 // Use a new validity dictionary for each test set. 236 PostfixEvaluator<unsigned int>::DictionaryValidityType assigned; 237 238 for (unsigned int evaluate_test_index = 0; 239 evaluate_test_index < evaluate_test_count; 240 ++evaluate_test_index) { 241 const EvaluateTest *evaluate_test = &evaluate_tests[evaluate_test_index]; 242 243 // Do the test. 244 bool result = postfix_evaluator.Evaluate(evaluate_test->expression, 245 &assigned); 246 if (result != evaluate_test->evaluable) { 247 fprintf(stderr, "FAIL: evaluate set %d/%d, test %d/%d, " 248 "expression \"%s\", expected %s, observed %s\n", 249 evaluate_test_set_index, evaluate_test_set_count, 250 evaluate_test_index, evaluate_test_count, 251 evaluate_test->expression.c_str(), 252 evaluate_test->evaluable ? "evaluable" : "not evaluable", 253 result ? "evaluted" : "not evaluated"); 254 return false; 255 } 256 } 257 258 // Validate the results. 259 for (map<string, unsigned int>::const_iterator validate_iterator = 260 evaluate_test_set->validate_data->begin(); 261 validate_iterator != evaluate_test_set->validate_data->end(); 262 ++validate_iterator) { 263 const string identifier = validate_iterator->first; 264 unsigned int expected_value = validate_iterator->second; 265 266 map<string, unsigned int>::const_iterator dictionary_iterator = 267 evaluate_test_set->dictionary->find(identifier); 268 269 // The identifier must exist in the dictionary. 270 if (dictionary_iterator == evaluate_test_set->dictionary->end()) { 271 fprintf(stderr, "FAIL: evaluate test set %d/%d, " 272 "validate identifier \"%s\", " 273 "expected %d, observed not found\n", 274 evaluate_test_set_index, evaluate_test_set_count, 275 identifier.c_str(), expected_value); 276 return false; 277 } 278 279 // The value in the dictionary must be the same as the expected value. 280 unsigned int observed_value = dictionary_iterator->second; 281 if (expected_value != observed_value) { 282 fprintf(stderr, "FAIL: evaluate test set %d/%d, " 283 "validate identifier \"%s\", " 284 "expected %d, observed %d\n", 285 evaluate_test_set_index, evaluate_test_set_count, 286 identifier.c_str(), expected_value, observed_value); 287 return false; 288 } 289 290 // The value must be set in the "assigned" dictionary if it was a 291 // variable. It must not have been assigned if it was a constant. 292 bool expected_assigned = identifier[0] == '$'; 293 bool observed_assigned = false; 294 PostfixEvaluator<unsigned int>::DictionaryValidityType::const_iterator 295 iterator_assigned = assigned.find(identifier); 296 if (iterator_assigned != assigned.end()) { 297 observed_assigned = iterator_assigned->second; 298 } 299 if (expected_assigned != observed_assigned) { 300 fprintf(stderr, "FAIL: evaluate test set %d/%d, " 301 "validate assignment of \"%s\", " 302 "expected %d, observed %d\n", 303 evaluate_test_set_index, evaluate_test_set_count, 304 identifier.c_str(), expected_assigned, observed_assigned); 305 return false; 306 } 307 } 308 } 309 310 // EvaluateForValue tests. 311 PostfixEvaluator<unsigned int>::DictionaryType dictionary_2; 312 dictionary_2["$ebp"] = 0xbfff0010; 313 dictionary_2["$eip"] = 0x10000000; 314 dictionary_2["$esp"] = 0xbfff0000; 315 dictionary_2[".cbSavedRegs"] = 4; 316 dictionary_2[".cbParams"] = 4; 317 dictionary_2[".raSearchStart"] = 0xbfff0020; 318 const EvaluateForValueTest evaluate_for_value_tests_2[] = { 319 { "28907223", true, 28907223 }, // simple constant 320 { "89854293 40010015 +", true, 89854293 + 40010015 }, // arithmetic 321 { "-870245 8769343 +", true, 7899098 }, // negative constants 322 { "$ebp $esp - $eip +", true, 0x10000010 }, // variable references 323 { "18929794 34015074", false, 0 }, // too many values 324 { "$ebp $ebp 4 - =", false, 0 }, // too few values 325 { "$new $eip = $new", true, 0x10000000 }, // make new variable 326 { "$new 4 +", true, 0x10000004 }, // see prior assignments 327 { ".cfa 42 = 10", false, 0 } // can't set constants 328 }; 329 const int evaluate_for_value_tests_2_size 330 = (sizeof (evaluate_for_value_tests_2) 331 / sizeof (evaluate_for_value_tests_2[0])); 332 map<string, unsigned int> validate_data_2; 333 validate_data_2["$eip"] = 0x10000000; 334 validate_data_2["$ebp"] = 0xbfff000c; 335 validate_data_2["$esp"] = 0xbfff0000; 336 validate_data_2["$new"] = 0x10000000; 337 validate_data_2[".cbSavedRegs"] = 4; 338 validate_data_2[".cbParams"] = 4; 339 validate_data_2[".raSearchStart"] = 0xbfff0020; 340 341 postfix_evaluator.set_dictionary(&dictionary_2); 342 for (int i = 0; i < evaluate_for_value_tests_2_size; i++) { 343 const EvaluateForValueTest *test = &evaluate_for_value_tests_2[i]; 344 unsigned int result; 345 if (postfix_evaluator.EvaluateForValue(test->expression, &result) 346 != test->evaluable) { 347 fprintf(stderr, "FAIL: evaluate for value test %d, " 348 "expected evaluation to %s, but it %s\n", 349 i, test->evaluable ? "succeed" : "fail", 350 test->evaluable ? "failed" : "succeeded"); 351 return false; 352 } 353 if (test->evaluable && result != test->value) { 354 fprintf(stderr, "FAIL: evaluate for value test %d, " 355 "expected value to be 0x%x, but it was 0x%x\n", 356 i, test->value, result); 357 return false; 358 } 359 } 360 361 for (map<string, unsigned int>::iterator v = validate_data_2.begin(); 362 v != validate_data_2.end(); v++) { 363 map<string, unsigned int>::iterator a = dictionary_2.find(v->first); 364 if (a == dictionary_2.end()) { 365 fprintf(stderr, "FAIL: evaluate for value dictionary check: " 366 "expected dict[\"%s\"] to be 0x%x, but it was unset\n", 367 v->first.c_str(), v->second); 368 return false; 369 } else if (a->second != v->second) { 370 fprintf(stderr, "FAIL: evaluate for value dictionary check: " 371 "expected dict[\"%s\"] to be 0x%x, but it was 0x%x\n", 372 v->first.c_str(), v->second, a->second); 373 return false; 374 } 375 dictionary_2.erase(a); 376 } 377 378 map<string, unsigned int>::iterator remaining = dictionary_2.begin(); 379 if (remaining != dictionary_2.end()) { 380 fprintf(stderr, "FAIL: evaluation of test expressions put unexpected " 381 "values in dictionary:\n"); 382 for (; remaining != dictionary_2.end(); remaining++) 383 fprintf(stderr, " dict[\"%s\"] == 0x%x\n", 384 remaining->first.c_str(), remaining->second); 385 return false; 386 } 387 388 return true; 389} 390 391 392} // namespace 393 394 395int main(int argc, char **argv) { 396 BPLOG_INIT(&argc, &argv); 397 398 return RunTests() ? 0 : 1; 399} 400