1// Copyright 2017 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <mini-process/mini-process.h> 6#include <unittest/unittest.h> 7#include <zircon/process.h> 8#include <zircon/status.h> 9#include <zircon/syscalls.h> 10#include <zircon/syscalls/exception.h> 11#include <zircon/syscalls/object.h> 12 13#include <inttypes.h> 14#include <limits.h> 15#include <stdio.h> 16#include <stdlib.h> 17 18#include <fbl/algorithm.h> 19 20#define LOCAL_TRACE 0 21#define LTRACEF(str, x...) \ 22 do { \ 23 if (LOCAL_TRACE) { \ 24 printf("%s:%d: " str, __func__, __LINE__, ##x); \ 25 } \ 26 } while (0) 27 28namespace { 29 30// A function that returns a handle to get the info of. 31// Typically get_test_process, get_test_job, zx_process_self, zx_job_default. 32typedef zx_handle_t (*handle_source_fn)(); 33 34bool handle_valid_on_valid_handle_succeeds() { 35 BEGIN_TEST; 36 EXPECT_EQ(zx_object_get_info(zx_process_self(), ZX_INFO_HANDLE_VALID, 37 nullptr, 0, nullptr, nullptr), 38 ZX_OK); 39 END_TEST; 40} 41 42bool handle_valid_on_closed_handle_fails() { 43 BEGIN_TEST; 44 // Create an event and show that it's valid. 45 zx_handle_t event; 46 ASSERT_EQ(zx_event_create(0u, &event), ZX_OK); 47 EXPECT_EQ(zx_object_get_info(event, ZX_INFO_HANDLE_VALID, 48 nullptr, 0, nullptr, nullptr), 49 ZX_OK); 50 51 // Close the handle and show that it becomes invalid. 52 zx_handle_close(event); 53 EXPECT_NE(zx_object_get_info(event, ZX_INFO_HANDLE_VALID, 54 nullptr, 0, nullptr, nullptr), 55 ZX_OK); 56 END_TEST; 57} 58 59// Tests that ZX_INFO_TASK_STATS seems to work. 60bool task_stats_smoke() { 61 BEGIN_TEST; 62 zx_info_task_stats_t info; 63 ASSERT_EQ(zx_object_get_info(zx_process_self(), ZX_INFO_TASK_STATS, 64 &info, sizeof(info), nullptr, nullptr), 65 ZX_OK); 66 ASSERT_GT(info.mem_private_bytes, 0u); 67 ASSERT_GT(info.mem_shared_bytes, 0u); 68 ASSERT_GE(info.mem_mapped_bytes, 69 info.mem_private_bytes + info.mem_shared_bytes); 70 71 ASSERT_GT(info.mem_scaled_shared_bytes, 0u); 72 ASSERT_GT(info.mem_shared_bytes, info.mem_scaled_shared_bytes); 73 END_TEST; 74} 75 76// Structs to keep track of VMARs/mappings in the test child process. 77typedef struct test_mapping { 78 uintptr_t base; 79 size_t size; 80 uint32_t flags; // ZX_INFO_MAPS_MMU_FLAG_PERM_{READ,WRITE,EXECUTE} 81} test_mapping_t; 82 83// A VMO that the test process maps or has a handle to. 84typedef struct test_vmo { 85 zx_koid_t koid; 86 size_t size; 87 uint32_t flags; // ZX_INFO_VMO_VIA_{HANDLE,MAPPING} 88} test_vmo_t; 89 90typedef struct test_mapping_info { 91 uintptr_t vmar_base; 92 size_t vmar_size; 93 size_t num_mappings; 94 test_mapping_t* mappings; // num_mappings entries 95 size_t num_vmos; 96 test_vmo_t* vmos; // num_vmos entries 97} test_mapping_info_t; 98 99// Gets the koid of the object pointed to by |handle|. 100zx_status_t get_koid(zx_handle_t handle, zx_koid_t* koid) { 101 zx_info_handle_basic_t info; 102 zx_status_t s = zx_object_get_info(handle, ZX_INFO_HANDLE_BASIC, 103 &info, sizeof(info), nullptr, nullptr); 104 if (s == ZX_OK) { 105 *koid = info.koid; 106 } 107 return s; 108} 109 110// Returns a process singleton. ZX_INFO_PROCESS_MAPS can't run on the current 111// process, so tests should use this instead. 112// This handle is leaked, and we expect our process teardown to clean it up 113// naturally. 114zx_handle_t get_test_process_etc(const test_mapping_info_t** info) { 115 static zx_handle_t test_process = ZX_HANDLE_INVALID; 116 static test_mapping_info_t* test_info = nullptr; 117 118 if (info != nullptr) { 119 *info = nullptr; 120 } 121 if (test_process == ZX_HANDLE_INVALID) { 122 // Create a VMO whose handle we'll give to the test process. 123 // It will not be mapped into the test process's VMAR. 124 const size_t unmapped_vmo_size = PAGE_SIZE; 125 zx_handle_t unmapped_vmo; 126 zx_status_t s = zx_vmo_create( 127 unmapped_vmo_size, /* options */ 0u, &unmapped_vmo); 128 if (s != ZX_OK) { 129 EXPECT_EQ(s, ZX_OK, "zx_vmo_create"); // Poison the test. 130 return ZX_HANDLE_INVALID; 131 } 132 zx_koid_t unmapped_vmo_koid; 133 s = get_koid(unmapped_vmo, &unmapped_vmo_koid); 134 if (s != ZX_OK) { 135 EXPECT_EQ(s, ZX_OK, "get_koid"); 136 return ZX_HANDLE_INVALID; 137 } 138 // Try to set the name, but ignore any errors. 139 static const char unmapped_vmo_name[] = "test:unmapped"; 140 zx_object_set_property(unmapped_vmo, ZX_PROP_NAME, 141 unmapped_vmo_name, sizeof(unmapped_vmo_name)); 142 143 // Failures from here on will start to leak handles, but they'll 144 // be cleaned up when this binary exits. 145 146 zx_handle_t process; 147 zx_handle_t vmar; 148 static const char pname[] = "object-info-minipr"; 149 s = zx_process_create(zx_job_default(), pname, sizeof(pname), 150 /* options */ 0u, &process, &vmar); 151 if (s != ZX_OK) { 152 EXPECT_EQ(s, ZX_OK, "zx_process_create"); 153 return ZX_HANDLE_INVALID; 154 } 155 156 zx_handle_t thread; 157 static const char tname[] = "object-info-minith"; 158 s = zx_thread_create(process, tname, sizeof(tname), 159 /* options */ 0u, &thread); 160 if (s != ZX_OK) { 161 EXPECT_EQ(s, ZX_OK, "zx_thread_create"); 162 return ZX_HANDLE_INVALID; 163 } 164 165 zx_handle_t minip_channel; 166 // Start the process before we mess with the VMAR, 167 // so we don't step on the mapping done by start_mini_process_etc. 168 s = start_mini_process_etc(process, thread, vmar, unmapped_vmo, 169 &minip_channel); 170 if (s != ZX_OK) { 171 EXPECT_EQ(s, ZX_OK, "start_mini_process_etc"); 172 return ZX_HANDLE_INVALID; 173 } 174 unmapped_vmo = ZX_HANDLE_INVALID; // Transferred to the test process. 175 zx_handle_close(minip_channel); 176 177 // Create a child VMAR and a mapping under it, so we have 178 // something interesting to look at when getting the process's 179 // memory maps. After this, the process maps should at least contain: 180 // 181 // Root Aspace 182 // - Root VMAR 183 // - Code+stack mapping created by start_mini_process_etc 184 // - Sub VMAR created below 185 // - kNumMappings mappings created below 186 187 static const size_t kNumMappings = 8; 188 // Leaked on failure. Never freed on success. 189 test_mapping_info_t* ti = (test_mapping_info_t*)malloc(sizeof(*ti)); 190 ti->num_mappings = kNumMappings; 191 ti->mappings = 192 (test_mapping_t*)malloc(kNumMappings * sizeof(test_mapping_t)); 193 194 // Big enough to fit all of the mappings with some slop. 195 ti->vmar_size = PAGE_SIZE * kNumMappings * 16; 196 zx_handle_t sub_vmar; 197 s = zx_vmar_allocate(vmar, 198 ZX_VM_CAN_MAP_READ | 199 ZX_VM_CAN_MAP_WRITE | 200 ZX_VM_CAN_MAP_EXECUTE, 201 /* offset */ 0, 202 ti->vmar_size, 203 &sub_vmar, &ti->vmar_base); 204 if (s != ZX_OK) { 205 EXPECT_EQ(s, ZX_OK, "zx_vmar_allocate"); 206 return ZX_HANDLE_INVALID; 207 } 208 209 zx_handle_t vmo; 210 const size_t vmo_size = PAGE_SIZE * kNumMappings; 211 s = zx_vmo_create(vmo_size, /* options */ 0u, &vmo); 212 if (s != ZX_OK) { 213 EXPECT_EQ(s, ZX_OK, "zx_vmo_create"); 214 return ZX_HANDLE_INVALID; 215 } 216 zx_koid_t vmo_koid; 217 s = get_koid(vmo, &vmo_koid); 218 if (s != ZX_OK) { 219 EXPECT_EQ(s, ZX_OK, "get_koid"); 220 return ZX_HANDLE_INVALID; 221 } 222 // Try to set the name, but ignore any errors. 223 static const char vmo_name[] = "test:mapped"; 224 zx_object_set_property(vmo, ZX_PROP_NAME, vmo_name, sizeof(vmo_name)); 225 226 // Record the VMOs now that we have both of them. 227 ti->num_vmos = 2; 228 ti->vmos = (test_vmo_t*)malloc(2 * sizeof(test_vmo_t)); 229 ti->vmos[0].koid = unmapped_vmo_koid; 230 ti->vmos[0].size = unmapped_vmo_size; 231 ti->vmos[0].flags = ZX_INFO_VMO_VIA_HANDLE; 232 ti->vmos[1].koid = vmo_koid; 233 ti->vmos[1].size = vmo_size; 234 ti->vmos[1].flags = ZX_INFO_VMO_VIA_MAPPING; 235 236 // Map each page of the VMO to some arbitray location in the VMAR. 237 for (size_t i = 0; i < kNumMappings; i++) { 238 test_mapping_t* m = &ti->mappings[i]; 239 m->size = PAGE_SIZE; 240 241 // Pick flags for this mapping; cycle through different 242 // combinations for the test. Must always have READ set 243 // to be mapped. 244 m->flags = ZX_VM_PERM_READ; 245 if (i & 1) { 246 m->flags |= ZX_VM_PERM_WRITE; 247 } 248 if (i & 2) { 249 m->flags |= ZX_VM_PERM_EXECUTE; 250 } 251 252 s = zx_vmar_map(sub_vmar, m->flags, /* vmar_offset (ignored) */ 0, 253 vmo, /* vmo_offset */ i * PAGE_SIZE, 254 /* len */ PAGE_SIZE, 255 &m->base); 256 if (s != ZX_OK) { 257 char msg[32]; 258 snprintf(msg, sizeof(msg), "zx_vmar_map: [%zd]", i); 259 EXPECT_EQ(s, ZX_OK, msg); 260 return ZX_HANDLE_INVALID; 261 } 262 } 263 zx_handle_close(vmo); // Kept alive by the VMAR. 264 zx_handle_close(sub_vmar); // Kept alive by the process. 265 266 test_process = process; 267 test_info = ti; 268 } 269 if (info != nullptr) { 270 *info = test_info; 271 } 272 return test_process; 273} 274 275zx_handle_t get_test_process() { 276 return get_test_process_etc(nullptr); 277} 278 279// Tests that ZX_INFO_PROCESS_MAPS seems to work. 280bool process_maps_smoke() { 281 BEGIN_TEST; 282 const test_mapping_info_t* test_info; 283 const zx_handle_t process = get_test_process_etc(&test_info); 284 ASSERT_NONNULL(test_info, "get_test_process_etc"); 285 286 // Buffer big enough to read all of the test process's map entries. 287 const size_t bufsize = test_info->num_mappings * 4 * sizeof(zx_info_maps_t); 288 zx_info_maps_t* maps = (zx_info_maps_t*)malloc(bufsize); 289 290 // Read the map entries. 291 size_t actual; 292 size_t avail; 293 ASSERT_EQ(zx_object_get_info(process, ZX_INFO_PROCESS_MAPS, 294 maps, bufsize, 295 &actual, &avail), 296 ZX_OK); 297 EXPECT_EQ(actual, avail, "Should have read all entries"); 298 299 // The first two entries should always be the ASpace and root VMAR. 300 ASSERT_GE(actual, 2u, "Root aspace/vmar missing?"); 301 EXPECT_EQ(maps[0].type, (uint32_t)ZX_INFO_MAPS_TYPE_ASPACE); 302 EXPECT_EQ(maps[0].depth, 0u, "ASpace depth"); 303 EXPECT_GT(maps[0].size, 1u * 1024 * 1024 * 1024 * 1024, "ASpace size"); 304 EXPECT_EQ(maps[1].type, (uint32_t)ZX_INFO_MAPS_TYPE_VMAR); 305 EXPECT_EQ(maps[1].depth, 1u, "Root VMAR depth"); 306 EXPECT_GT(maps[1].size, 1u * 1024 * 1024 * 1024 * 1024, "Root VMAR size"); 307 308 // Look for the VMAR and all of the mappings we created. 309 bool saw_vmar = false; // Whether we've seen our VMAR. 310 bool under_vmar = false; // If we're looking at children of our VMAR. 311 size_t vmar_depth = 0; 312 uint32_t saw_mapping = 0u; // bitmask of mapping indices we've seen. 313 ASSERT_LT(test_info->num_mappings, 32u); 314 315 LTRACEF("\n"); 316 for (size_t i = 2; i < actual; i++) { 317 zx_info_maps_t* entry = maps + i; 318 char msg[128]; 319 snprintf(msg, sizeof(msg), 320 "[%2zd] %*stype:%u base:0x%" PRIx64 " size:%" PRIu64, 321 i, (int)(entry->depth - 2) * 2, "", 322 entry->type, entry->base, entry->size); 323 LTRACEF("%s\n", msg); 324 // All entries should be children of the root VMAR. 325 EXPECT_GT(entry->depth, 1u, msg); 326 EXPECT_TRUE(entry->type >= ZX_INFO_MAPS_TYPE_ASPACE && 327 entry->type <= ZX_INFO_MAPS_TYPE_MAPPING, 328 msg); 329 330 if (entry->type == ZX_INFO_MAPS_TYPE_VMAR && 331 entry->base == test_info->vmar_base && 332 entry->size == test_info->vmar_size) { 333 saw_vmar = true; 334 under_vmar = true; 335 vmar_depth = entry->depth; 336 } else if (under_vmar) { 337 if (entry->depth <= vmar_depth) { 338 under_vmar = false; 339 vmar_depth = 0; 340 } else { 341 // |entry| should be a child mapping of our VMAR. 342 EXPECT_EQ((uint32_t)ZX_INFO_MAPS_TYPE_MAPPING, entry->type, 343 msg); 344 // The mapping should fit inside the VMAR. 345 EXPECT_LE(test_info->vmar_base, entry->base, msg); 346 EXPECT_LE(entry->base + entry->size, 347 test_info->vmar_base + test_info->vmar_size, 348 msg); 349 // Look for it in the expected mappings. 350 bool found = false; 351 for (size_t j = 0; j < test_info->num_mappings; j++) { 352 const test_mapping_t* t = &test_info->mappings[j]; 353 if (t->base == entry->base && t->size == entry->size) { 354 // Make sure we don't see duplicates. 355 EXPECT_EQ(0u, saw_mapping & (1 << j), msg); 356 saw_mapping |= 1 << j; 357 EXPECT_EQ(t->flags, entry->u.mapping.mmu_flags, msg); 358 found = true; 359 break; 360 } 361 } 362 EXPECT_TRUE(found, msg); 363 } 364 } 365 } 366 367 // Make sure we saw our VMAR and all of our mappings. 368 EXPECT_TRUE(saw_vmar); 369 EXPECT_EQ((uint32_t)(1 << test_info->num_mappings) - 1, saw_mapping); 370 371 // Do one more read with a short buffer to test actual < avail. 372 const size_t bufsize2 = actual * 3 / 4 * sizeof(zx_info_maps_t); 373 zx_info_maps_t* maps2 = (zx_info_maps_t*)malloc(bufsize2); 374 size_t actual2; 375 size_t avail2; 376 ASSERT_EQ(zx_object_get_info(process, ZX_INFO_PROCESS_MAPS, 377 maps2, bufsize2, 378 &actual2, &avail2), 379 ZX_OK); 380 EXPECT_LT(actual2, avail2); 381 // mini-process is very simple, and won't have modified its own memory 382 // maps since the previous dump. Its "committed_pages" values could be 383 // different, though. 384 EXPECT_EQ(avail, avail2); 385 LTRACEF("\n"); 386 EXPECT_GT(actual2, 3u); // Make sure we're looking at something. 387 for (size_t i = 0; i < actual2; i++) { 388 zx_info_maps_t* e1 = maps + i; 389 zx_info_maps_t* e2 = maps2 + i; 390 char msg[128]; 391 snprintf(msg, sizeof(msg), 392 "[%2zd] %*stype:%u/%u base:0x%" PRIx64 "/0x%" PRIx64 393 " size:%" PRIu64 "/%" PRIu64, 394 i, (int)e1->depth * 2, "", 395 e1->type, e2->type, e1->base, e2->base, e1->size, e2->size); 396 LTRACEF("%s\n", msg); 397 EXPECT_EQ(e1->base, e2->base, msg); 398 EXPECT_EQ(e1->size, e2->size, msg); 399 EXPECT_EQ(e1->depth, e2->depth, msg); 400 EXPECT_EQ(e1->type, e2->type, msg); 401 if (e1->type == e2->type && e2->type == ZX_INFO_MAPS_TYPE_MAPPING) { 402 EXPECT_EQ(e1->u.mapping.mmu_flags, e2->u.mapping.mmu_flags, msg); 403 } 404 } 405 406 free(maps); 407 free(maps2); 408 END_TEST; 409} 410 411template <uint32_t Topic, typename EntryType> 412bool self_fails() { 413 BEGIN_TEST; 414 EntryType entries[2]; 415 size_t actual; 416 size_t avail; 417 // It's illegal to look at your own entries, because the output buffer 418 // lives inside the address space that's being examined. 419 EXPECT_EQ(zx_object_get_info(zx_process_self(), Topic, 420 entries, sizeof(entries), &actual, &avail), 421 ZX_ERR_ACCESS_DENIED); 422 END_TEST; 423} 424 425template <uint32_t Topic, typename EntryType> 426bool invalid_handle_fails() { 427 BEGIN_TEST; 428 EntryType entries[2]; 429 size_t actual; 430 size_t avail; 431 // Passing ZX_HANDLE_INVALID should fail. 432 EXPECT_EQ(zx_object_get_info(ZX_HANDLE_INVALID, Topic, 433 entries, sizeof(entries), &actual, &avail), 434 ZX_ERR_BAD_HANDLE); 435 END_TEST; 436} 437 438template <uint32_t Topic, typename EntryType, handle_source_fn GetWrongHandle> 439bool wrong_handle_type_fails() { 440 BEGIN_TEST; 441 EntryType entries[2]; 442 size_t actual; 443 size_t avail; 444 // Passing a handle to an unsupported object type should fail. 445 EXPECT_NE(zx_object_get_info(GetWrongHandle(), Topic, 446 entries, sizeof(entries), &actual, &avail), 447 ZX_OK); 448 END_TEST; 449} 450 451template <uint32_t Topic, typename EntryType, 452 handle_source_fn GetHandle, zx_rights_t MissingRights> 453bool missing_rights_fails() { 454 BEGIN_TEST; 455 // Call should succeed with the default rights. 456 zx_handle_t obj = GetHandle(); 457 EntryType entries[2]; 458 size_t actual; 459 size_t avail; 460 EXPECT_EQ(zx_object_get_info(obj, Topic, 461 entries, sizeof(entries), &actual, &avail), 462 ZX_OK); 463 464 // Get the test object handle rights. 465 zx_info_handle_basic_t hi; 466 ASSERT_EQ(zx_object_get_info(obj, ZX_INFO_HANDLE_BASIC, 467 &hi, sizeof(hi), nullptr, nullptr), 468 ZX_OK); 469 char msg[32]; 470 snprintf(msg, sizeof(msg), "rights 0x%" PRIx32, hi.rights); 471 EXPECT_EQ(hi.rights & MissingRights, MissingRights, msg); 472 473 // Create a handle without the important rights. 474 zx_handle_t handle; 475 ASSERT_EQ(zx_handle_duplicate(obj, hi.rights & ~MissingRights, &handle), 476 ZX_OK); 477 478 // Call should fail without these rights. 479 EXPECT_EQ(zx_object_get_info(handle, Topic, 480 entries, sizeof(entries), &actual, &avail), 481 ZX_ERR_ACCESS_DENIED); 482 483 zx_handle_close(handle); 484 END_TEST; 485} 486 487template <uint32_t Topic, typename EntryType, handle_source_fn GetHandle> 488bool single_zero_buffer_fails() { 489 BEGIN_TEST; 490 EntryType entry; 491 size_t actual; 492 size_t avail; 493 // Passing a zero-sized buffer to a topic that expects a single 494 // in/out entry should fail. 495 EXPECT_EQ(zx_object_get_info(GetHandle(), Topic, 496 &entry, // buffer 497 0, // len 498 &actual, &avail), 499 ZX_ERR_BUFFER_TOO_SMALL); 500 EXPECT_EQ(0u, actual); 501 EXPECT_GT(avail, 0u); 502 END_TEST; 503} 504 505template <uint32_t Topic, handle_source_fn GetHandle> 506bool multi_zero_buffer_succeeds() { 507 BEGIN_TEST; 508 size_t actual; 509 size_t avail; 510 // Passing a zero-sized null buffer to a topic that can handle multiple 511 // in/out entries should succeed. 512 EXPECT_EQ(zx_object_get_info(GetHandle(), Topic, 513 nullptr, // buffer 514 0, // len 515 &actual, &avail), 516 ZX_OK); 517 EXPECT_EQ(0u, actual); 518 EXPECT_GT(avail, 0u); 519 END_TEST; 520} 521 522template <uint32_t Topic, typename EntryType, handle_source_fn GetHandle> 523bool short_buffer_succeeds() { 524 BEGIN_TEST; 525 EntryType entries[1]; 526 size_t actual; 527 size_t avail; 528 // Passing a buffer shorter than avail should succeed. 529 EXPECT_EQ(zx_object_get_info(GetHandle(), Topic, 530 entries, 531 sizeof(entries), 532 &actual, &avail), 533 ZX_OK); 534 EXPECT_EQ(1u, actual); 535 EXPECT_GT(avail, actual); 536 END_TEST; 537} 538 539template <uint32_t Topic, typename EntryType, handle_source_fn GetHandle> 540bool null_avail_actual_succeeds() { 541 BEGIN_TEST; 542 EntryType entries[2]; 543 EXPECT_EQ(zx_object_get_info(GetHandle(), Topic, 544 entries, sizeof(entries), 545 nullptr, // actual 546 nullptr), // avail 547 ZX_OK); 548 END_TEST; 549} 550 551template <uint32_t Topic, typename EntryType, handle_source_fn GetHandle> 552bool bad_buffer_fails() { 553 BEGIN_TEST; 554 size_t actual; 555 size_t avail; 556 EXPECT_EQ(zx_object_get_info(GetHandle(), Topic, 557 // Bad buffer pointer value. 558 (EntryType*)1, 559 sizeof(EntryType), 560 &actual, &avail), 561 ZX_ERR_INVALID_ARGS); 562 END_TEST; 563} 564 565// Tests the behavior when passing a buffer that starts in mapped 566// memory but crosses into unmapped memory. 567template <uint32_t Topic, typename EntryType, handle_source_fn GetHandle> 568bool partially_unmapped_buffer_fails() { 569 BEGIN_TEST; 570 // Create a two-page VMAR. 571 zx_handle_t vmar; 572 uintptr_t vmar_addr; 573 ASSERT_EQ(zx_vmar_allocate(zx_vmar_root_self(), 574 ZX_VM_CAN_MAP_READ | 575 ZX_VM_CAN_MAP_WRITE | 576 ZX_VM_CAN_MAP_SPECIFIC, 577 0, 2 * PAGE_SIZE, 578 &vmar, &vmar_addr), 579 ZX_OK); 580 581 // Create a one-page VMO. 582 zx_handle_t vmo; 583 ASSERT_EQ(zx_vmo_create(PAGE_SIZE, 0, &vmo), ZX_OK); 584 585 // Map the first page of the VMAR. 586 uintptr_t vmo_addr; 587 ASSERT_EQ(zx_vmar_map(vmar, 588 ZX_VM_SPECIFIC | 589 ZX_VM_PERM_READ | 590 ZX_VM_PERM_WRITE, 591 0, vmo, 0, PAGE_SIZE, &vmo_addr), 592 ZX_OK); 593 ASSERT_EQ(vmar_addr, vmo_addr); 594 595 // Point to a spot in the mapped page just before the unmapped region: 596 // the first entry will hit mapped memory, the second entry will hit 597 // unmapped memory. 598 EntryType* entries = (EntryType*)(vmo_addr + PAGE_SIZE) - 1; 599 600 size_t actual; 601 size_t avail; 602 EXPECT_EQ(zx_object_get_info(GetHandle(), Topic, 603 entries, sizeof(EntryType) * 4, 604 &actual, &avail), 605 // Bad user buffer should return ZX_ERR_INVALID_ARGS. 606 ZX_ERR_INVALID_ARGS); 607 608 zx_vmar_destroy(vmar); 609 zx_handle_close(vmar); 610 zx_handle_close(vmo); 611 END_TEST; 612} 613 614template <uint32_t Topic, typename EntryType, handle_source_fn GetHandle> 615bool bad_actual_fails() { 616 BEGIN_TEST; 617 EntryType entries[2]; 618 size_t avail; 619 EXPECT_EQ(zx_object_get_info(GetHandle(), Topic, 620 entries, sizeof(entries), 621 // Bad actual pointer value. 622 (size_t*)1, 623 &avail), 624 ZX_ERR_INVALID_ARGS); 625 END_TEST; 626} 627 628template <uint32_t Topic, typename EntryType, handle_source_fn GetHandle> 629bool bad_avail_fails() { 630 BEGIN_TEST; 631 EntryType entries[2]; 632 size_t actual; 633 EXPECT_EQ(zx_object_get_info(GetHandle(), Topic, 634 entries, sizeof(entries), &actual, 635 // Bad available pointer value. 636 (size_t*)1), 637 ZX_ERR_INVALID_ARGS); 638 END_TEST; 639} 640 641// Tests that ZX_INFO_PROCESS_VMOS seems to work. 642bool process_vmos_smoke() { 643 BEGIN_TEST; 644 const test_mapping_info_t* test_info; 645 const zx_handle_t process = get_test_process_etc(&test_info); 646 ASSERT_NONNULL(test_info, "get_test_process_etc"); 647 648 // Buffer big enough to read all of the test process's VMO entries. 649 // There'll be one per mapping, one for the unmapped VMO, plus some 650 // extras (at least the vDSO and the mini-process stack). 651 const size_t bufsize = 652 (test_info->num_mappings + 1 + 8) * sizeof(zx_info_vmo_t); 653 zx_info_vmo_t* vmos = (zx_info_vmo_t*)malloc(bufsize); 654 655 // Read the VMO entries. 656 size_t actual; 657 size_t avail; 658 ASSERT_EQ(zx_object_get_info(process, ZX_INFO_PROCESS_VMOS, 659 vmos, bufsize, 660 &actual, &avail), 661 ZX_OK); 662 EXPECT_EQ(actual, avail, "Should have read all entries"); 663 664 // Look for the expected VMOs. 665 uint32_t saw_vmo = 0u; // Bitmask of VMO indices we've seen 666 ASSERT_LT(test_info->num_vmos, 32u); 667 668 LTRACEF("\n"); 669 for (size_t i = 0; i < actual; i++) { 670 zx_info_vmo_t* entry = vmos + i; 671 char msg[128]; 672 snprintf(msg, sizeof(msg), 673 "[%2zd] koid:%" PRIu64 " name:'%s' size:%" PRIu64 674 " flags:0x%" PRIx32, 675 i, entry->koid, entry->name, entry->size_bytes, entry->flags); 676 LTRACEF("%s\n", msg); 677 678 // Look for it in the expected VMOs. We won't find all VMOs here, 679 // since we don't track the vDSO or mini-process stack. 680 for (size_t j = 0; j < test_info->num_vmos; j++) { 681 const test_vmo_t* t = &test_info->vmos[j]; 682 if (t->koid == entry->koid && t->size == entry->size_bytes) { 683 // These checks aren't appropriate for all VMOs. 684 // The VMOs we track are: 685 // - Only mapped or via handle, not both 686 // - Not clones 687 // - Not shared 688 EXPECT_EQ(entry->parent_koid, 0u, msg); 689 EXPECT_EQ(entry->num_children, 0u, msg); 690 EXPECT_EQ(entry->share_count, 1u, msg); 691 EXPECT_EQ(t->flags & entry->flags, t->flags, msg); 692 if (entry->flags & ZX_INFO_VMO_VIA_HANDLE) { 693 EXPECT_EQ(entry->num_mappings, 0u, msg); 694 } else { 695 EXPECT_NE(entry->flags & ZX_INFO_VMO_VIA_MAPPING, 0u, msg); 696 EXPECT_EQ( 697 entry->num_mappings, test_info->num_mappings, msg); 698 } 699 EXPECT_EQ(entry->flags & ZX_INFO_VMO_IS_COW_CLONE, 0u, msg); 700 701 saw_vmo |= 1 << j; // Duplicates are fine and expected 702 break; 703 } 704 } 705 706 // All of our VMOs should be paged, not physical. 707 EXPECT_EQ(ZX_INFO_VMO_TYPE(entry->flags), ZX_INFO_VMO_TYPE_PAGED, msg); 708 709 // Each entry should be via either map or handle, but not both. 710 // NOTE: This could change in the future, but currently reflects 711 // the way things work. 712 const uint32_t kViaMask = 713 ZX_INFO_VMO_VIA_HANDLE | ZX_INFO_VMO_VIA_MAPPING; 714 EXPECT_NE(entry->flags & kViaMask, kViaMask, msg); 715 716 // TODO(dbort): Test more fields/flags of zx_info_vmo_t by adding some 717 // clones, shared VMOs, mapped+handle VMOs, physical VMOs if possible. 718 // All but committed_bytes should be predictable. 719 } 720 721 // Make sure we saw all of the expected VMOs. 722 EXPECT_EQ((uint32_t)(1 << test_info->num_vmos) - 1, saw_vmo); 723 724 // Do one more read with a short buffer to test actual < avail. 725 const size_t bufsize2 = actual * 3 / 4 * sizeof(zx_info_vmo_t); 726 zx_info_vmo_t* vmos2 = (zx_info_vmo_t*)malloc(bufsize2); 727 size_t actual2; 728 size_t avail2; 729 ASSERT_EQ(zx_object_get_info(process, ZX_INFO_PROCESS_VMOS, 730 vmos2, bufsize2, 731 &actual2, &avail2), 732 ZX_OK); 733 EXPECT_LT(actual2, avail2); 734 // mini-process is very simple, and won't have modified its own set of VMOs 735 // since the previous dump. 736 EXPECT_EQ(avail, avail2); 737 LTRACEF("\n"); 738 EXPECT_GT(actual2, 3u); // Make sure we're looking at something. 739 for (size_t i = 0; i < actual2; i++) { 740 zx_info_vmo_t* e1 = vmos + i; 741 zx_info_vmo_t* e2 = vmos2 + i; 742 char msg[128]; 743 snprintf(msg, sizeof(msg), 744 "[%2zd] koid:%" PRIu64 "/%" PRIu64 " name:'%s'/'%s' " 745 "size:%" PRIu64 "/%" PRIu64 " flags:0x%" PRIx32 "/0x%" PRIx32, 746 i, e1->koid, e2->koid, e1->name, e2->name, 747 e1->size_bytes, e2->size_bytes, e1->flags, e2->flags); 748 LTRACEF("%s\n", msg); 749 EXPECT_EQ(e1->koid, e2->koid, msg); 750 EXPECT_EQ(e1->size_bytes, e2->size_bytes, msg); 751 EXPECT_EQ(e1->flags, e2->flags, msg); 752 if (e1->flags == e2->flags && e2->flags & ZX_INFO_VMO_VIA_HANDLE) { 753 EXPECT_EQ(e1->handle_rights, e2->handle_rights, msg); 754 } 755 } 756 757 free(vmos); 758 free(vmos2); 759 END_TEST; 760} 761 762// ZX_INFO_JOB_PROCESS/ZX_INFO_JOB_CHILDREN tests 763 764// Returns a job with the structure: 765// - returned job 766// - child process 1 767// - child process 2 768// - child process 3 (kTestJobChildProcs) 769// - child job 1 770// - grandchild process 1.1 771// - grandchild job 1.1 772// - child job 2 (kTestJobChildJobs) 773// - grandchild process 2.1 774// - grandchild job 2.1 775const size_t kTestJobChildProcs = 3; 776const size_t kTestJobChildJobs = 2; 777zx_handle_t get_test_job() { 778 static zx_handle_t test_job = ZX_HANDLE_INVALID; 779 780 if (test_job == ZX_HANDLE_INVALID) { 781 char msg[64]; 782 zx_handle_t root; 783 zx_status_t s = zx_job_create(zx_job_default(), 0, &root); 784 if (s != ZX_OK) { 785 EXPECT_EQ(s, ZX_OK, "zx_job_create"); // Poison the test. 786 return ZX_HANDLE_INVALID; 787 } 788 for (size_t i = 0; i < kTestJobChildProcs; i++) { 789 zx_handle_t proc; 790 zx_handle_t vmar; 791 s = zx_process_create(root, "child", 6, 0, &proc, &vmar); 792 if (s != ZX_OK) { 793 snprintf(msg, sizeof(msg), "zx_process_create(child %zu)", i); 794 goto fail; 795 } 796 } 797 for (size_t i = 0; i < kTestJobChildJobs; i++) { 798 zx_handle_t job; 799 s = zx_job_create(root, 0, &job); 800 if (s != ZX_OK) { 801 snprintf(msg, sizeof(msg), "zx_job_create(child %zu)", i); 802 goto fail; 803 } 804 zx_handle_t proc; 805 zx_handle_t vmar; 806 s = zx_process_create(job, "grandchild", 6, 0, &proc, &vmar); 807 if (s != ZX_OK) { 808 snprintf(msg, sizeof(msg), "zx_process_create(grandchild)"); 809 goto fail; 810 } 811 zx_handle_t subjob; 812 s = zx_job_create(job, 0, &subjob); 813 if (s != ZX_OK) { 814 snprintf(msg, sizeof(msg), "zx_job_create(grandchild)"); 815 goto fail; 816 } 817 } 818 819 if (false) { 820 fail: 821 EXPECT_EQ(s, ZX_OK, msg); // Poison the test 822 zx_task_kill(root); // Clean up all tasks; leaks handles 823 return ZX_HANDLE_INVALID; 824 } 825 test_job = root; 826 } 827 828 return test_job; 829} 830 831// The jobch_helper_* (job child helper) functions allow testing both 832// ZX_INFO_JOB_PROCESS and ZX_INFO_JOB_CHILDREN. 833bool jobch_helper_smoke(uint32_t topic, size_t expected_count) { 834 BEGIN_TEST; 835 zx_koid_t koids[32]; 836 size_t actual; 837 size_t avail; 838 EXPECT_EQ(zx_object_get_info(get_test_job(), topic, 839 koids, sizeof(koids), &actual, &avail), 840 ZX_OK); 841 EXPECT_EQ(expected_count, actual); 842 EXPECT_EQ(expected_count, avail); 843 844 // All returned koids should produce a valid handle when passed to 845 // zx_object_get_child. 846 for (size_t i = 0; i < actual; i++) { 847 char msg[32]; 848 snprintf(msg, sizeof(msg), "koid %zu", koids[i]); 849 zx_handle_t h = ZX_HANDLE_INVALID; 850 EXPECT_EQ(zx_object_get_child(get_test_job(), koids[i], 851 ZX_RIGHT_SAME_RIGHTS, &h), 852 ZX_OK, msg); 853 zx_handle_close(h); 854 } 855 END_TEST; 856} 857 858bool job_processes_smoke() { 859 return jobch_helper_smoke(ZX_INFO_JOB_PROCESSES, kTestJobChildProcs); 860} 861 862bool job_children_smoke() { 863 return jobch_helper_smoke(ZX_INFO_JOB_CHILDREN, kTestJobChildJobs); 864} 865 866uint32_t handle_count_or_zero(zx_handle_t handle) { 867 zx_info_handle_count_t info; 868 if (ZX_OK != zx_object_get_info( 869 handle, ZX_INFO_HANDLE_COUNT, &info, sizeof(info), nullptr, nullptr)) { 870 return 0u; 871 } 872 return info.handle_count; 873} 874 875bool handle_count_valid() { 876 // We create an event and check that ZX_INFO_HANDLE_COUNT stats at 1 and 877 // goes up for each new handle minted from it and goes down for each handle 878 // closed. 879 zx_handle_t event[4]; 880 ASSERT_EQ(zx_event_create(0u, &event[0]), ZX_OK); 881 EXPECT_EQ(handle_count_or_zero(event[0]), 1u); 882 883 for (size_t i = 1; i != fbl::count_of(event); ++i) { 884 ASSERT_EQ(zx_handle_duplicate( 885 event[0], ZX_RIGHT_SIGNAL, &event[i]), 886 ZX_OK); 887 EXPECT_EQ(handle_count_or_zero(event[0]), i + 1); 888 } 889 890 for (size_t i = fbl::count_of(event) - 1; i != 0; --i) { 891 ASSERT_EQ(zx_handle_close(event[i]), ZX_OK); 892 EXPECT_EQ(handle_count_or_zero(event[0]), i); 893 } 894 895 zx_handle_close(event[0]); 896 return true; 897} 898 899bool handle_stats_control() { 900 zx_info_process_handle_stats_t info; 901 zx_status_t status = zx_object_get_info(zx_process_self(), ZX_INFO_PROCESS_HANDLE_STATS, 902 &info, sizeof(info), nullptr, nullptr); 903 ASSERT_EQ(status, ZX_OK); 904 EXPECT_EQ(info.handle_count[ZX_OBJ_TYPE_NONE], 0); 905 EXPECT_GT(info.handle_count[ZX_OBJ_TYPE_PROCESS], 0); 906 EXPECT_GT(info.handle_count[ZX_OBJ_TYPE_THREAD], 0); 907 EXPECT_GT(info.handle_count[ZX_OBJ_TYPE_VMO], 0); 908 EXPECT_EQ(info.handle_count[ZX_OBJ_TYPE_INTERRUPT], 0); 909 910 uint32_t channel_count = info.handle_count[ZX_OBJ_TYPE_CHANNEL]; 911 912 zx_handle_t h1, h2; 913 status = zx_channel_create(0, &h1, &h2); 914 ASSERT_EQ(status, ZX_OK); 915 916 status = zx_object_get_info(zx_process_self(), ZX_INFO_PROCESS_HANDLE_STATS, 917 &info, sizeof(info), nullptr, nullptr); 918 ASSERT_EQ(status, ZX_OK); 919 EXPECT_EQ(info.handle_count[ZX_OBJ_TYPE_CHANNEL], channel_count + 2); 920 zx_handle_close(h1); 921 zx_handle_close(h2); 922 return true; 923} 924 925} // namespace 926 927// Tests that should pass for any topic. Use the wrappers below instead of 928// calling this directly. 929#define _RUN_COMMON_TESTS(topic, entry_type, get_handle) \ 930 RUN_TEST((invalid_handle_fails<topic, entry_type>)); \ 931 RUN_TEST((null_avail_actual_succeeds<topic, entry_type, get_handle>)); \ 932 RUN_TEST((bad_buffer_fails<topic, entry_type, get_handle>)); \ 933 RUN_TEST((bad_actual_fails<topic, entry_type, get_handle>)); \ 934 RUN_TEST((bad_avail_fails<topic, entry_type, get_handle>)) 935 936// Tests that should pass for any topic that expects a single entry in its 937// in/out buffer. 938#define RUN_SINGLE_ENTRY_TESTS(topic, entry_type, get_handle) \ 939 _RUN_COMMON_TESTS(topic, entry_type, get_handle); \ 940 RUN_TEST((single_zero_buffer_fails<topic, entry_type, get_handle>)) 941 942// Tests that should pass for any topic that can handle multiple entries in its 943// in/out buffer. 944#define RUN_MULTI_ENTRY_TESTS(topic, entry_type, get_handle) \ 945 _RUN_COMMON_TESTS(topic, entry_type, get_handle); \ 946 RUN_TEST((multi_zero_buffer_succeeds<topic, get_handle>)); \ 947 RUN_TEST((short_buffer_succeeds<topic, entry_type, get_handle>)); \ 948 RUN_TEST((partially_unmapped_buffer_fails<topic, entry_type, get_handle>)) 949 950BEGIN_TEST_CASE(object_info_tests) 951 952// ZX_INFO_HANDLE_VALID is an oddball that doesn't care about its buffer, 953// so we can't use the normal topic test suites. 954RUN_TEST(handle_valid_on_valid_handle_succeeds); 955RUN_TEST(handle_valid_on_closed_handle_fails); 956RUN_TEST((invalid_handle_fails<ZX_INFO_HANDLE_VALID, void*>)); 957 958RUN_TEST(task_stats_smoke); 959RUN_SINGLE_ENTRY_TESTS(ZX_INFO_TASK_STATS, zx_info_task_stats_t, zx_process_self); 960RUN_TEST((wrong_handle_type_fails<ZX_INFO_TASK_STATS, zx_info_task_stats_t, get_test_job>)); 961RUN_TEST((wrong_handle_type_fails<ZX_INFO_TASK_STATS, zx_info_task_stats_t, zx_thread_self>)); 962 963RUN_TEST(process_maps_smoke); 964RUN_MULTI_ENTRY_TESTS(ZX_INFO_PROCESS_MAPS, zx_info_maps_t, get_test_process); 965RUN_TEST((self_fails<ZX_INFO_PROCESS_MAPS, zx_info_maps_t>)) 966RUN_TEST((wrong_handle_type_fails<ZX_INFO_PROCESS_MAPS, zx_info_maps_t, get_test_job>)); 967RUN_TEST((wrong_handle_type_fails<ZX_INFO_PROCESS_MAPS, zx_info_maps_t, zx_thread_self>)); 968RUN_TEST((missing_rights_fails<ZX_INFO_PROCESS_MAPS, zx_info_maps_t, get_test_process, 969 ZX_RIGHT_INSPECT>)); 970 971RUN_TEST(process_vmos_smoke); 972RUN_MULTI_ENTRY_TESTS(ZX_INFO_PROCESS_VMOS, zx_info_vmo_t, get_test_process); 973RUN_TEST((self_fails<ZX_INFO_PROCESS_VMOS, zx_info_vmo_t>)) 974RUN_TEST((wrong_handle_type_fails<ZX_INFO_PROCESS_VMOS, zx_info_vmo_t, get_test_job>)); 975RUN_TEST((wrong_handle_type_fails<ZX_INFO_PROCESS_VMOS, zx_info_vmo_t, zx_thread_self>)); 976RUN_TEST((missing_rights_fails<ZX_INFO_PROCESS_VMOS, zx_info_vmo_t, get_test_process, 977 ZX_RIGHT_INSPECT>)); 978 979RUN_TEST(job_processes_smoke); 980RUN_MULTI_ENTRY_TESTS(ZX_INFO_JOB_PROCESSES, zx_koid_t, get_test_job); 981RUN_TEST((wrong_handle_type_fails<ZX_INFO_JOB_PROCESSES, zx_koid_t, get_test_process>)); 982RUN_TEST((wrong_handle_type_fails<ZX_INFO_JOB_PROCESSES, zx_koid_t, zx_thread_self>)); 983RUN_TEST((missing_rights_fails<ZX_INFO_JOB_PROCESSES, zx_koid_t, get_test_job, 984 ZX_RIGHT_ENUMERATE>)); 985 986RUN_TEST(job_children_smoke); 987RUN_MULTI_ENTRY_TESTS(ZX_INFO_JOB_CHILDREN, zx_koid_t, get_test_job); 988RUN_TEST((wrong_handle_type_fails<ZX_INFO_JOB_CHILDREN, zx_koid_t, get_test_process>)); 989RUN_TEST((wrong_handle_type_fails<ZX_INFO_JOB_CHILDREN, zx_koid_t, zx_thread_self>)); 990RUN_TEST((missing_rights_fails<ZX_INFO_JOB_CHILDREN, zx_koid_t, get_test_job, 991 ZX_RIGHT_ENUMERATE>)); 992 993// Basic tests for all other topics. 994 995RUN_SINGLE_ENTRY_TESTS(ZX_INFO_HANDLE_BASIC, zx_info_handle_basic_t, get_test_job); 996RUN_SINGLE_ENTRY_TESTS(ZX_INFO_HANDLE_BASIC, zx_info_handle_basic_t, get_test_process); 997RUN_SINGLE_ENTRY_TESTS(ZX_INFO_HANDLE_BASIC, zx_info_handle_basic_t, zx_thread_self); 998RUN_SINGLE_ENTRY_TESTS(ZX_INFO_HANDLE_BASIC, zx_info_handle_basic_t, zx_vmar_root_self); 999 1000RUN_SINGLE_ENTRY_TESTS(ZX_INFO_HANDLE_COUNT, zx_info_handle_count_t, zx_thread_self); 1001 1002RUN_SINGLE_ENTRY_TESTS(ZX_INFO_PROCESS, zx_info_process_t, get_test_process); 1003RUN_TEST((wrong_handle_type_fails<ZX_INFO_PROCESS, zx_info_process_t, get_test_job>)); 1004RUN_TEST((wrong_handle_type_fails<ZX_INFO_PROCESS, zx_info_process_t, zx_thread_self>)); 1005 1006RUN_SINGLE_ENTRY_TESTS(ZX_INFO_VMAR, zx_info_vmar_t, zx_vmar_root_self); 1007RUN_TEST((wrong_handle_type_fails<ZX_INFO_VMAR, zx_info_vmar_t, get_test_job>)); 1008RUN_TEST((wrong_handle_type_fails<ZX_INFO_VMAR, zx_info_vmar_t, get_test_process>)); 1009RUN_TEST((wrong_handle_type_fails<ZX_INFO_VMAR, zx_info_vmar_t, zx_thread_self>)); 1010 1011RUN_SINGLE_ENTRY_TESTS(ZX_INFO_THREAD, zx_info_thread_t, zx_thread_self); 1012RUN_TEST((wrong_handle_type_fails<ZX_INFO_THREAD, zx_info_thread_t, get_test_job>)); 1013RUN_TEST((wrong_handle_type_fails<ZX_INFO_THREAD, zx_info_thread_t, get_test_process>)); 1014 1015RUN_SINGLE_ENTRY_TESTS(ZX_INFO_THREAD_STATS, zx_info_thread_stats_t, zx_thread_self); 1016RUN_TEST((wrong_handle_type_fails<ZX_INFO_THREAD_STATS, zx_info_thread_t, get_test_job>)); 1017RUN_TEST((wrong_handle_type_fails<ZX_INFO_THREAD_STATS, zx_info_thread_t, get_test_process>)); 1018 1019// ZX_INFO_PROCESS_THREADS tests. 1020// TODO(dbort): Use RUN_MULTI_ENTRY_TESTS instead. |short_buffer_succeeds| and 1021// |partially_unmapped_buffer_fails| currently fail because those tests expect 1022// avail > 1, but the test process only has one thread and it's not trivial to 1023// add more. 1024RUN_TEST((invalid_handle_fails<ZX_INFO_PROCESS_THREADS, zx_koid_t>)); 1025RUN_TEST((null_avail_actual_succeeds<ZX_INFO_PROCESS_THREADS, zx_koid_t, get_test_process>)); 1026RUN_TEST((bad_buffer_fails<ZX_INFO_PROCESS_THREADS, zx_koid_t, get_test_process>)); 1027RUN_TEST((bad_actual_fails<ZX_INFO_PROCESS_THREADS, zx_koid_t, get_test_process>)); 1028RUN_TEST((bad_avail_fails<ZX_INFO_PROCESS_THREADS, zx_koid_t, get_test_process>)) 1029RUN_TEST((multi_zero_buffer_succeeds<ZX_INFO_PROCESS_THREADS, get_test_process>)); 1030 1031// Skip most tests for ZX_INFO_THREAD_EXCEPTION_REPORT, which is tested 1032// elsewhere and requires the target thread to be in a certain state. 1033RUN_TEST((invalid_handle_fails<ZX_INFO_THREAD_EXCEPTION_REPORT, zx_exception_report_t>)); 1034 1035// TODO(dbort): Test resource topics 1036// RUN_MULTI_ENTRY_TESTS(ZX_INFO_CPU_STATS, zx_info_cpu_stats_t, get_root_resource); 1037// RUN_SINGLE_ENTRY_TESTS(ZX_INFO_KMEM_STATS, zx_info_kmem_stats_t, get_root_resource); 1038 1039RUN_TEST(handle_count_valid); 1040 1041RUN_TEST(handle_stats_control); 1042 1043END_TEST_CASE(object_info_tests) 1044 1045#ifndef BUILD_COMBINED_TESTS 1046int main(int argc, char** argv) { 1047 return unittest_run_all_tests(argc, argv) ? 0 : -1; 1048} 1049#endif 1050