1 // Copyright 2006 Google Inc. All Rights Reserved.
2 // Author: nsanders, menderico
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
8 // http://www.apache.org/licenses/LICENSE-2.0
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
16 // os.cc : os and machine specific implementation
17 // This file includes an abstracted interface
18 // for linux-distro specific and HW specific
25 #include <linux/types.h>
31 #include <sys/ioctl.h>
33 #include <sys/types.h>
41 #define SHM_HUGETLB 04000 // remove when glibc defines it
47 // This file must work with autoconf on its public version,
48 // so these includes are correct.
50 #include "error_diag.h"
52 // OsLayer initialization.
57 min_hugepages_bytes_ = 0;
59 use_hugepages_ = false;
60 use_posix_shm_ = false;
61 dynamic_mapped_shmem_ = false;
64 time_initialized_ = 0;
70 num_cpus_per_node_ = 0;
72 err_log_callback_ = 0;
73 error_injection_ = false;
76 address_mode_ = sizeof(pvoid) * 8;
81 use_flush_page_cache_ = false;
87 delete error_diagnoser_;
90 // OsLayer initialization.
91 bool OsLayer::Initialize() {
92 time_initialized_ = time(NULL);
93 // Detect asm support.
98 num_cpus_ = sysconf(_SC_NPROCESSORS_ONLN);
99 num_cpus_per_node_ = num_cpus_ / num_nodes_;
101 logprintf(5, "Log: %d nodes, %d cpus.\n", num_nodes_, num_cpus_);
102 sat_assert(CPU_SETSIZE >= num_cpus_);
103 cpu_sets_.resize(num_nodes_);
104 cpu_sets_valid_.resize(num_nodes_);
105 // Create error diagnoser.
106 error_diagnoser_ = new ErrorDiag();
107 if (!error_diagnoser_->set_os(this))
112 // Machine type detected. Can we implement all these functions correctly?
113 bool OsLayer::IsSupported() {
115 // There are no explicitly supported systems in open source version.
119 // This is the default empty implementation.
120 // SAT won't report full error information.
124 int OsLayer::AddressMode() {
125 // Detect 32/64 bit binary.
127 return sizeof(pvoid) * 8;
130 // Translates user virtual to physical address.
131 uint64 OsLayer::VirtualToPhysical(void *vaddr) {
133 off64_t off = ((uintptr_t)vaddr) / getpagesize() * 8;
134 int fd = open(kPagemapPath, O_RDONLY);
135 if (fd < 0 || lseek64(fd, off, SEEK_SET) != off || read(fd, &frame, 8) != 8) {
137 string errtxt = ErrorString(err);
138 logprintf(0, "Error: failed to access %s with errno %d (%s)\n",
139 kPagemapPath, err, errtxt.c_str());
145 if (!(frame & (1LL << 63)) || (frame & (1LL << 62)))
147 shift = (frame >> 55) & 0x3f;
148 frame = (frame & 0x007fffffffffffffLL) << shift;
149 return frame | ((uintptr_t)vaddr & ((1LL << shift) - 1));
152 // Returns the HD device that contains this file.
153 string OsLayer::FindFileDevice(string filename) {
157 // Returns a list of locations corresponding to HD devices.
158 list<string> OsLayer::FindFileDevices() {
159 // No autodetection on unknown systems.
160 list<string> locations;
165 // Get HW core features from cpuid instruction.
166 void OsLayer::GetFeatures() {
167 #if defined(STRESSAPPTEST_CPU_X86_64) || defined(STRESSAPPTEST_CPU_I686)
168 // CPUID features documented at:
169 // http://www.sandpile.org/ia32/cpuid.htm
171 __asm__ __volatile__ (
172 # if defined(STRESSAPPTEST_CPU_I686) && defined(__PIC__)
181 "=a" (ax), "=c" (cx), "=d" (dx) : "a" (1));
182 has_clflush_ = (dx >> 19) & 1;
183 has_sse2_ = (dx >> 26) & 1;
185 logprintf(9, "Log: has clflush: %s, has sse2: %s\n",
186 has_clflush_ ? "true" : "false",
187 has_sse2_ ? "true" : "false");
188 #elif defined(STRESSAPPTEST_CPU_PPC)
189 // All PPC implementations have cache flush instructions.
191 #elif defined(STRESSAPPTEST_CPU_ARMV7A)
192 #warning "Unsupported CPU type ARMV7A: unable to determine feature set."
194 #warning "Unsupported CPU type: unable to determine feature set."
199 // Enable FlushPageCache to be functional instead of a NOP.
200 void OsLayer::ActivateFlushPageCache(void) {
201 logprintf(9, "Log: page cache will be flushed as needed\n");
202 use_flush_page_cache_ = true;
205 // Flush the page cache to ensure reads come from the disk.
206 bool OsLayer::FlushPageCache(void) {
207 if (!use_flush_page_cache_)
210 // First, ask the kernel to write the cache to the disk.
213 // Second, ask the kernel to empty the cache by writing "1" to
214 // "/proc/sys/vm/drop_caches".
215 static const char *drop_caches_file = "/proc/sys/vm/drop_caches";
216 int dcfile = open(drop_caches_file, O_WRONLY);
219 string errtxt = ErrorString(err);
220 logprintf(3, "Log: failed to open %s - err %d (%s)\n",
221 drop_caches_file, err, errtxt.c_str());
225 ssize_t bytes_written = write(dcfile, "1", 1);
228 if (bytes_written != 1) {
230 string errtxt = ErrorString(err);
231 logprintf(3, "Log: failed to write %s - err %d (%s)\n",
232 drop_caches_file, err, errtxt.c_str());
239 // We need to flush the cacheline here.
240 void OsLayer::Flush(void *vaddr) {
241 // Use the generic flush. This function is just so we can override
242 // this if we are so inclined.
248 // Run C or ASM copy as appropriate..
249 bool OsLayer::AdlerMemcpyWarm(uint64 *dstmem, uint64 *srcmem,
250 unsigned int size_in_bytes,
251 AdlerChecksum *checksum) {
253 return AdlerMemcpyAsm(dstmem, srcmem, size_in_bytes, checksum);
255 return AdlerMemcpyWarmC(dstmem, srcmem, size_in_bytes, checksum);
260 // Translate user virtual to physical address.
261 int OsLayer::FindDimm(uint64 addr, char *buf, int len) {
263 snprintf(tmpbuf, sizeof(tmpbuf), "DIMM Unknown");
264 snprintf(buf, len, "%s", tmpbuf);
269 // Classifies addresses according to "regions"
270 // This isn't really implemented meaningfully here..
271 int32 OsLayer::FindRegion(uint64 addr) {
272 static bool warned = false;
274 if (regionsize_ == 0) {
275 regionsize_ = totalmemsize_ / 8;
276 if (regionsize_ < 512 * kMegabyte)
277 regionsize_ = 512 * kMegabyte;
278 regioncount_ = totalmemsize_ / regionsize_;
279 if (regioncount_ < 1) regioncount_ = 1;
282 int32 region_num = addr / regionsize_;
283 if (region_num >= regioncount_) {
285 logprintf(0, "Log: region number %d exceeds region count %d\n",
286 region_num, regioncount_);
289 region_num = region_num % regioncount_;
294 // Report which cores are associated with a given region.
295 cpu_set_t *OsLayer::FindCoreMask(int32 region) {
296 sat_assert(region >= 0);
297 region %= num_nodes_;
298 if (!cpu_sets_valid_[region]) {
299 CPU_ZERO(&cpu_sets_[region]);
300 for (int i = 0; i < num_cpus_per_node_; ++i) {
301 CPU_SET(i + region * num_cpus_per_node_, &cpu_sets_[region]);
303 cpu_sets_valid_[region] = true;
304 logprintf(5, "Log: Region %d mask 0x%s\n",
305 region, FindCoreMaskFormat(region).c_str());
307 return &cpu_sets_[region];
310 // Return cores associated with a given region in hex string.
311 string OsLayer::FindCoreMaskFormat(int32 region) {
312 cpu_set_t* mask = FindCoreMask(region);
313 string format = cpuset_format(mask);
314 if (format.size() < 8)
315 format = string(8 - format.size(), '0') + format;
319 // Report an error in an easily parseable way.
320 bool OsLayer::ErrorReport(const char *part, const char *symptom, int count) {
321 time_t now = time(NULL);
322 int ttf = now - time_initialized_;
323 logprintf(0, "Report Error: %s : %s : %d : %ds\n", symptom, part, count, ttf);
327 // Read the number of hugepages out of the kernel interface in proc.
328 int64 OsLayer::FindHugePages() {
331 // This is a kernel interface to query the numebr of hugepages
332 // available in the system.
333 static const char *hugepages_info_file = "/proc/sys/vm/nr_hugepages";
334 int hpfile = open(hugepages_info_file, O_RDONLY);
336 ssize_t bytes_read = read(hpfile, buf, 64);
339 if (bytes_read <= 0) {
340 logprintf(12, "Log: /proc/sys/vm/nr_hugepages "
341 "read did not provide data\n");
345 if (bytes_read == 64) {
346 logprintf(0, "Process Error: /proc/sys/vm/nr_hugepages "
347 "is surprisingly large\n");
351 // Add a null termintation to be string safe.
352 buf[bytes_read] = '\0';
353 // Read the page count.
354 int64 pages = strtoull(buf, NULL, 10); // NOLINT
359 int64 OsLayer::FindFreeMemSize() {
362 if (totalmemsize_ > 0)
363 return totalmemsize_;
365 int64 pages = sysconf(_SC_PHYS_PAGES);
366 int64 avpages = sysconf(_SC_AVPHYS_PAGES);
367 int64 pagesize = sysconf(_SC_PAGESIZE);
368 int64 physsize = pages * pagesize;
369 int64 avphyssize = avpages * pagesize;
371 // Assume 2MB hugepages.
372 int64 hugepagesize = FindHugePages() * 2 * kMegabyte;
374 if ((pages == -1) || (pagesize == -1)) {
375 logprintf(0, "Process Error: sysconf could not determine memory size.\n");
379 // We want to leave enough stuff for things to run.
380 // If the user specified a minimum amount of memory to expect, require that.
381 // Otherwise, if more than 2GB is present, leave 192M + 5% for other stuff.
382 // If less than 2GB is present use 85% of what's available.
383 // These are fairly arbitrary numbers that seem to work OK.
385 // TODO(nsanders): is there a more correct way to determine target
387 if (hugepagesize > 0 && min_hugepages_bytes_ > 0) {
388 minsize = min_hugepages_bytes_;
389 } else if (physsize < 2048LL * kMegabyte) {
390 minsize = ((pages * 85) / 100) * pagesize;
392 minsize = ((pages * 95) / 100) * pagesize - (192 * kMegabyte);
395 // Use hugepage sizing if available.
396 if (hugepagesize > 0) {
397 if (hugepagesize < minsize) {
398 logprintf(0, "Procedural Error: Not enough hugepages. "
399 "%lldMB available < %lldMB required.\n",
400 hugepagesize / kMegabyte,
401 minsize / kMegabyte);
402 // Require the calculated minimum amount of memory.
405 // Require that we get all hugepages.
409 // Require the calculated minimum amount of memory.
413 logprintf(5, "Log: Total %lld MB. Free %lld MB. Hugepages %lld MB. "
414 "Targeting %lld MB (%lld%%)\n",
415 physsize / kMegabyte,
416 avphyssize / kMegabyte,
417 hugepagesize / kMegabyte,
419 size * 100 / physsize);
421 totalmemsize_ = size;
425 // Allocates all memory available.
426 int64 OsLayer::AllocateAllMem() {
427 int64 length = FindFreeMemSize();
428 bool retval = AllocateTestMem(length, 0);
435 // Allocate the target memory. This may be from malloc, hugepage pool
436 // or other platform specific sources.
437 bool OsLayer::AllocateTestMem(int64 length, uint64 paddr_base) {
438 // Try hugepages first.
441 sat_assert(length >= 0);
444 logprintf(0, "Process Error: non zero paddr_base %#llx is not supported,"
445 " ignore.\n", paddr_base);
447 // Determine optimal memory allocation path.
448 bool prefer_hugepages = false;
449 bool prefer_posix_shm = false;
450 bool prefer_dynamic_mapping = false;
452 // Are there enough hugepages?
453 int64 hugepagesize = FindHugePages() * 2 * kMegabyte;
454 // TODO(nsanders): Is there enough /dev/shm? Is there enough free memeory?
455 if ((length >= 1400LL * kMegabyte) && (address_mode_ == 32)) {
456 prefer_dynamic_mapping = true;
457 prefer_posix_shm = true;
458 logprintf(3, "Log: Prefer POSIX shared memory allocation.\n");
459 logprintf(3, "Log: You may need to run "
460 "'sudo mount -o remount,size=100\% /dev/shm.'\n");
461 } else if (hugepagesize >= length) {
462 prefer_hugepages = true;
463 logprintf(3, "Log: Prefer using hugepace allocation.\n");
465 logprintf(3, "Log: Prefer plain malloc memory allocation.\n");
468 #ifdef HAVE_SYS_SHM_H
469 // Allocate hugepage mapped memory.
470 if (prefer_hugepages) {
471 do { // Allow break statement.
475 if ((shmid = shmget(2, length,
476 SHM_HUGETLB | IPC_CREAT | SHM_R | SHM_W)) < 0) {
478 string errtxt = ErrorString(err);
479 logprintf(3, "Log: failed to allocate shared hugepage "
480 "object - err %d (%s)\n",
481 err, errtxt.c_str());
482 logprintf(3, "Log: sysctl -w vm.nr_hugepages=XXX allows hugepages.\n");
486 shmaddr = shmat(shmid, NULL, NULL);
487 if (shmaddr == reinterpret_cast<void*>(-1)) {
489 string errtxt = ErrorString(err);
490 logprintf(0, "Log: failed to attach shared "
491 "hugepage object - err %d (%s).\n",
492 err, errtxt.c_str());
493 if (shmctl(shmid, IPC_RMID, NULL) < 0) {
495 string errtxt = ErrorString(err);
496 logprintf(0, "Log: failed to remove shared "
497 "hugepage object - err %d (%s).\n",
498 err, errtxt.c_str());
502 use_hugepages_ = true;
505 logprintf(0, "Log: Using shared hugepage object 0x%x at %p.\n",
510 if ((!use_hugepages_) && prefer_posix_shm) {
513 void *shmaddr = NULL;
515 shm_object = shm_open("/stressapptest", O_CREAT | O_RDWR, S_IRWXU);
516 if (shm_object < 0) {
518 string errtxt = ErrorString(err);
519 logprintf(3, "Log: failed to allocate shared "
520 "smallpage object - err %d (%s)\n",
521 err, errtxt.c_str());
525 if (0 > ftruncate(shm_object, length)) {
527 string errtxt = ErrorString(err);
528 logprintf(3, "Log: failed to ftruncate shared "
529 "smallpage object - err %d (%s)\n",
530 err, errtxt.c_str());
534 // 32 bit linux apps can only use ~1.4G of address space.
535 // Use dynamic mapping for allocations larger than that.
536 // Currently perf hit is ~10% for this.
537 if (prefer_dynamic_mapping) {
538 dynamic_mapped_shmem_ = true;
540 // Do a full mapping here otherwise.
541 shmaddr = mmap64(NULL, length, PROT_READ | PROT_WRITE,
542 MAP_SHARED | MAP_NORESERVE | MAP_LOCKED | MAP_POPULATE,
544 if (shmaddr == reinterpret_cast<void*>(-1)) {
546 string errtxt = ErrorString(err);
547 logprintf(0, "Log: failed to map shared "
548 "smallpage object - err %d (%s).\n",
549 err, errtxt.c_str());
554 use_posix_shm_ = true;
557 char location_message[256] = "";
558 if (dynamic_mapped_shmem_) {
559 sprintf(location_message, "mapped as needed");
561 sprintf(location_message, "at %p", shmaddr);
563 logprintf(0, "Log: Using posix shared memory object 0x%x %s.\n",
564 shm_object, location_message);
566 shm_unlink("/stressapptest");
568 #endif // HAVE_SYS_SHM_H
570 if (!use_hugepages_ && !use_posix_shm_) {
571 // Use memalign to ensure that blocks are aligned enough for disk direct IO.
572 buf = static_cast<char*>(memalign(4096, length));
574 logprintf(0, "Log: Using memaligned allocation at %p.\n", buf);
576 logprintf(0, "Process Error: memalign returned 0\n");
577 if ((length >= 1499LL * kMegabyte) && (address_mode_ == 32)) {
578 logprintf(0, "Log: You are trying to allocate > 1.4G on a 32 "
579 "bit process. Please setup shared memory.\n");
585 if (buf || dynamic_mapped_shmem_) {
586 testmemsize_ = length;
591 return (buf != 0) || dynamic_mapped_shmem_;
594 // Free the test memory.
595 void OsLayer::FreeTestMem() {
597 if (use_hugepages_) {
598 #ifdef HAVE_SYS_SHM_H
600 shmctl(shmid_, IPC_RMID, NULL);
602 } else if (use_posix_shm_) {
603 if (!dynamic_mapped_shmem_) {
604 munmap(testmem_, testmemsize_);
616 // Prepare the target memory. It may requre mapping in, or this may be a noop.
617 void *OsLayer::PrepareTestMem(uint64 offset, uint64 length) {
618 sat_assert((offset + length) <= testmemsize_);
619 if (dynamic_mapped_shmem_) {
620 // TODO(nsanders): Check if we can support MAP_NONBLOCK,
621 // and evaluate performance hit from not using it.
623 void * mapping = mmap64(NULL, length, PROT_READ | PROT_WRITE,
624 MAP_SHARED | MAP_NORESERVE | MAP_LOCKED | MAP_POPULATE,
627 void * mapping = mmap(NULL, length, PROT_READ | PROT_WRITE,
628 MAP_SHARED | MAP_NORESERVE | MAP_LOCKED | MAP_POPULATE,
631 if (mapping == MAP_FAILED) {
632 string errtxt = ErrorString(errno);
633 logprintf(0, "Process Error: PrepareTestMem mmap64(%llx, %llx) failed. "
635 offset, length, errtxt.c_str());
641 return reinterpret_cast<void*>(reinterpret_cast<char*>(testmem_) + offset);
644 // Release the test memory resources, if any.
645 void OsLayer::ReleaseTestMem(void *addr, uint64 offset, uint64 length) {
646 if (dynamic_mapped_shmem_) {
647 int retval = munmap(addr, length);
649 string errtxt = ErrorString(errno);
650 logprintf(0, "Process Error: ReleaseTestMem munmap(%p, %llx) failed. "
652 addr, length, errtxt.c_str());
658 // No error polling on unknown systems.
659 int OsLayer::ErrorPoll() {
663 // Generally, poll for errors once per second.
664 void OsLayer::ErrorWait() {
669 // Open a PCI bus-dev-func as a file and return its file descriptor.
670 // Error is indicated by return value less than zero.
671 int OsLayer::PciOpen(int bus, int device, int function) {
674 snprintf(dev_file, sizeof(dev_file), "/proc/bus/pci/%02x/%02x.%x",
675 bus, device, function);
677 int fd = open(dev_file, O_RDWR);
679 logprintf(0, "Process Error: Unable to open PCI bus %d, device %d, "
680 "function %d (errno %d).\n",
681 bus, device, function, errno);
689 // Read and write functions to access PCI config.
690 uint32 OsLayer::PciRead(int fd, uint32 offset, int width) {
691 // Strict aliasing rules lawyers will cause data corruption
692 // on cast pointers in some gccs.
699 uint32 size = width / 8;
701 sat_assert((width == 32) || (width == 16) || (width == 8));
702 sat_assert(offset <= (256 - size));
704 if (lseek(fd, offset, SEEK_SET) < 0) {
705 logprintf(0, "Process Error: Can't seek %x\n", offset);
708 if (read(fd, &datacast, size) != static_cast<ssize_t>(size)) {
709 logprintf(0, "Process Error: Can't read %x\n", offset);
716 sat_assert(&(datacast.l8) == reinterpret_cast<uint8*>(&datacast));
719 sat_assert(&(datacast.l16) == reinterpret_cast<uint16*>(&datacast));
727 void OsLayer::PciWrite(int fd, uint32 offset, uint32 value, int width) {
728 // Strict aliasing rules lawyers will cause data corruption
729 // on cast pointers in some gccs.
736 uint32 size = width / 8;
738 sat_assert((width == 32) || (width == 16) || (width == 8));
739 sat_assert(offset <= (256 - size));
741 // Cram the data into the right alignment.
744 sat_assert(&(datacast.l8) == reinterpret_cast<uint8*>(&datacast));
747 sat_assert(&(datacast.l16) == reinterpret_cast<uint16*>(&datacast));
748 datacast.l16 = value;
750 datacast.l32 = value;
753 if (lseek(fd, offset, SEEK_SET) < 0) {
754 logprintf(0, "Process Error: Can't seek %x\n", offset);
757 if (write(fd, &datacast, size) != static_cast<ssize_t>(size)) {
758 logprintf(0, "Process Error: Can't write %x to %x\n", datacast.l32, offset);
768 int OsLayer::OpenMSR(uint32 core, uint32 address) {
770 snprintf(buf, sizeof(buf), "/dev/cpu/%d/msr", core);
771 int fd = open(buf, O_RDWR);
775 uint32 pos = lseek(fd, address, SEEK_SET);
776 if (pos != address) {
778 logprintf(5, "Log: can't seek to msr %x, cpu %d\n", address, core);
785 bool OsLayer::ReadMSR(uint32 core, uint32 address, uint64 *data) {
786 int fd = OpenMSR(core, address);
790 // Read from the msr.
791 bool res = (sizeof(*data) == read(fd, data, sizeof(*data)));
794 logprintf(5, "Log: Failed to read msr %x core %d\n", address, core);
801 bool OsLayer::WriteMSR(uint32 core, uint32 address, uint64 *data) {
802 int fd = OpenMSR(core, address);
807 bool res = (sizeof(*data) == write(fd, data, sizeof(*data)));
810 logprintf(5, "Log: Failed to write msr %x core %d\n", address, core);
817 // Extract bits [n+len-1, n] from a 32 bit word.
818 // so GetBitField(0x0f00, 8, 4) == 0xf.
819 uint32 OsLayer::GetBitField(uint32 val, uint32 n, uint32 len) {
820 return (val >> n) & ((1<<len) - 1);
823 // Generic CPU stress workload that would work on any CPU/Platform.
824 // Float-point array moving average calculation.
825 bool OsLayer::CpuStressWorkload() {
826 double float_arr[100];
828 unsigned int seed = 12345;
830 // Initialize array with random numbers.
831 for (int i = 0; i < 100; i++) {
833 float_arr[i] = rand_r(&seed);
834 if (rand_r(&seed) % 2)
835 float_arr[i] *= -1.0;
837 float_arr[i] = rand();
839 float_arr[i] *= -1.0;
843 // Calculate moving average.
844 for (int i = 0; i < 100000000; i++) {
846 (float_arr[i % 100] + float_arr[(i + 1) % 100] +
847 float_arr[(i + 99) % 100]) / 3;
848 sum += float_arr[i % 100];
851 // Artificial printf so the loops do not get optimized away.
853 logprintf(12, "Log: I'm Feeling Lucky!\n");
857 PCIDevices OsLayer::GetPCIDevices() {
858 PCIDevices device_list;
860 struct dirent *buf = new struct dirent();
861 struct dirent *entry;
862 dir = opendir(kSysfsPath);
864 logprintf(0, "Process Error: Cannot open %s", kSysfsPath);
865 while (readdir_r(dir, buf, &entry) == 0 && entry) {
867 unsigned int dev, func;
868 // ".", ".." or a special non-device perhaps.
869 if (entry->d_name[0] == '.')
872 device = new PCIDevice();
873 if (sscanf(entry->d_name, "%04x:%02hx:%02x.%d",
874 &device->domain, &device->bus, &dev, &func) < 4) {
875 logprintf(0, "Process Error: Couldn't parse %s", entry->d_name);
881 device->vendor_id = PCIGetValue(entry->d_name, "vendor");
882 device->device_id = PCIGetValue(entry->d_name, "device");
883 PCIGetResources(entry->d_name, device);
884 device_list.insert(device_list.end(), device);
891 int OsLayer::PCIGetValue(string name, string object) {
895 snprintf(filename, sizeof(filename), "%s/%s/%s", kSysfsPath,
896 name.c_str(), object.c_str());
897 fd = open(filename, O_RDONLY);
900 len = read(fd, buf, 256);
903 return strtol(buf, NULL, 0); // NOLINT
906 int OsLayer::PCIGetResources(string name, PCIDevice *device) {
914 snprintf(filename, sizeof(filename), "%s/%s/%s", kSysfsPath,
915 name.c_str(), "resource");
916 file = fopen(filename, "r");
918 logprintf(0, "Process Error: impossible to find resource file for %s",
922 for (i = 0; i < 6; i++) {
923 if (!fgets(buf, 256, file))
925 sscanf(buf, "%llx %llx", &start, &end); // NOLINT
928 size = end - start + 1;
929 device->base_addr[i] = start;
930 device->size[i] = size;