chiark / gitweb /
This patch replaces the existing OsLayer::VirtualToPhysical stub with
[stressapptest] / src / os.cc
1 // Copyright 2006 Google Inc. All Rights Reserved.
2 // Author: nsanders, menderico
3
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
7
8 //      http://www.apache.org/licenses/LICENSE-2.0
9
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.
15
16 // os.cc : os and machine specific implementation
17 // This file includes an abstracted interface
18 // for linux-distro specific and HW specific
19 // interfaces.
20
21 #include "os.h"
22
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <linux/types.h>
26 #include <malloc.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <sys/mman.h>
31 #include <sys/ioctl.h>
32 #include <sys/time.h>
33 #include <sys/types.h>
34 #include <sys/ipc.h>
35 #ifdef HAVE_SYS_SHM_H
36 #include <sys/shm.h>
37 #endif
38 #include <unistd.h>
39
40 #ifndef SHM_HUGETLB
41 #define SHM_HUGETLB      04000  // remove when glibc defines it
42 #endif
43
44 #include <string>
45 #include <list>
46
47 // This file must work with autoconf on its public version,
48 // so these includes are correct.
49 #include "sattypes.h"
50 #include "error_diag.h"
51
52 // OsLayer initialization.
53 OsLayer::OsLayer() {
54   testmem_ = 0;
55   testmemsize_ = 0;
56   totalmemsize_ = 0;
57   min_hugepages_bytes_ = 0;
58   normal_mem_ = true;
59   use_hugepages_ = false;
60   use_posix_shm_ = false;
61   dynamic_mapped_shmem_ = false;
62   shmid_ = 0;
63
64   time_initialized_ = 0;
65
66   regionsize_ = 0;
67   regioncount_ = 1;
68   num_cpus_ = 0;
69   num_nodes_ = 0;
70   num_cpus_per_node_ = 0;
71   error_diagnoser_ = 0;
72   err_log_callback_ = 0;
73   error_injection_ = false;
74
75   void *pvoid = 0;
76   address_mode_ = sizeof(pvoid) * 8;
77
78   has_clflush_ = false;
79   has_sse2_ = false;
80
81   use_flush_page_cache_ = false;
82 }
83
84 // OsLayer cleanup.
85 OsLayer::~OsLayer() {
86   if (error_diagnoser_)
87     delete error_diagnoser_;
88 }
89
90 // OsLayer initialization.
91 bool OsLayer::Initialize() {
92   time_initialized_ = time(NULL);
93   // Detect asm support.
94   GetFeatures();
95
96   if (num_cpus_ == 0) {
97     num_nodes_ = 1;
98     num_cpus_ = sysconf(_SC_NPROCESSORS_ONLN);
99     num_cpus_per_node_ = num_cpus_ / num_nodes_;
100   }
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))
108     return false;
109   return true;
110 }
111
112 // Machine type detected. Can we implement all these functions correctly?
113 bool OsLayer::IsSupported() {
114   if (kOpenSource) {
115     // There are no explicitly supported systems in open source version.
116     return true;
117   }
118
119   // This is the default empty implementation.
120   // SAT won't report full error information.
121   return false;
122 }
123
124 int OsLayer::AddressMode() {
125   // Detect 32/64 bit binary.
126   void *pvoid = 0;
127   return sizeof(pvoid) * 8;
128 }
129
130 // Translates user virtual to physical address.
131 uint64 OsLayer::VirtualToPhysical(void *vaddr) {
132   uint64 frame, shift;
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) {
136     int err = errno;
137     string errtxt = ErrorString(err);
138     logprintf(0, "Error: failed to access %s with errno %d (%s)\n",
139               kPagemapPath, err, errtxt.c_str());
140     if (fd >= 0)
141       close(fd);
142     return 0;
143   }
144   close(fd);
145   if (!(frame & (1LL << 63)) || (frame & (1LL << 62)))
146     return 0;
147   shift = (frame >> 55) & 0x3f;
148   frame = (frame & 0x007fffffffffffffLL) << shift;
149   return frame | ((uintptr_t)vaddr & ((1LL << shift) - 1));
150 }
151
152 // Returns the HD device that contains this file.
153 string OsLayer::FindFileDevice(string filename) {
154   return "hdUnknown";
155 }
156
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;
161   return locations;
162 }
163
164
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
170   int ax, bx, cx, dx;
171   __asm__ __volatile__ (
172       "cpuid": "=a" (ax), "=b" (bx), "=c" (cx), "=d" (dx) : "a" (1));
173   has_clflush_ = (dx >> 19) & 1;
174   has_sse2_ = (dx >> 26) & 1;
175
176   logprintf(9, "Log: has clflush: %s, has sse2: %s\n",
177             has_clflush_ ? "true" : "false",
178             has_sse2_ ? "true" : "false");
179 #elif defined(STRESSAPPTEST_CPU_PPC)
180   // All PPC implementations have cache flush instructions.
181   has_clflush_ = true;
182 #elif defined(STRESSAPPTEST_CPU_ARMV7A)
183 #warning "Unsupported CPU type ARMV7A: unable to determine feature set."
184 #else
185 #warning "Unsupported CPU type: unable to determine feature set."
186 #endif
187 }
188
189
190 // Enable FlushPageCache to be functional instead of a NOP.
191 void OsLayer::ActivateFlushPageCache(void) {
192   logprintf(9, "Log: page cache will be flushed as needed\n");
193   use_flush_page_cache_ = true;
194 }
195
196 // Flush the page cache to ensure reads come from the disk.
197 bool OsLayer::FlushPageCache(void) {
198   if (!use_flush_page_cache_)
199     return true;
200
201   // First, ask the kernel to write the cache to the disk.
202   sync();
203
204   // Second, ask the kernel to empty the cache by writing "1" to
205   // "/proc/sys/vm/drop_caches".
206   static const char *drop_caches_file = "/proc/sys/vm/drop_caches";
207   int dcfile = open(drop_caches_file, O_WRONLY);
208   if (dcfile < 0) {
209     int err = errno;
210     string errtxt = ErrorString(err);
211     logprintf(3, "Log: failed to open %s - err %d (%s)\n",
212               drop_caches_file, err, errtxt.c_str());
213     return false;
214   }
215
216   ssize_t bytes_written = write(dcfile, "1", 1);
217   close(dcfile);
218
219   if (bytes_written != 1) {
220     int err = errno;
221     string errtxt = ErrorString(err);
222     logprintf(3, "Log: failed to write %s - err %d (%s)\n",
223               drop_caches_file, err, errtxt.c_str());
224     return false;
225   }
226   return true;
227 }
228
229
230 // We need to flush the cacheline here.
231 void OsLayer::Flush(void *vaddr) {
232   // Use the generic flush. This function is just so we can override
233   // this if we are so inclined.
234   if (has_clflush_)
235     FastFlush(vaddr);
236 }
237
238
239 // Run C or ASM copy as appropriate..
240 bool OsLayer::AdlerMemcpyWarm(uint64 *dstmem, uint64 *srcmem,
241                               unsigned int size_in_bytes,
242                               AdlerChecksum *checksum) {
243   if (has_sse2_) {
244     return AdlerMemcpyAsm(dstmem, srcmem, size_in_bytes, checksum);
245   } else {
246     return AdlerMemcpyWarmC(dstmem, srcmem, size_in_bytes, checksum);
247   }
248 }
249
250
251 // Translate user virtual to physical address.
252 int OsLayer::FindDimm(uint64 addr, char *buf, int len) {
253   char tmpbuf[256];
254   snprintf(tmpbuf, sizeof(tmpbuf), "DIMM Unknown");
255   snprintf(buf, len, "%s", tmpbuf);
256   return 0;
257 }
258
259
260 // Classifies addresses according to "regions"
261 // This isn't really implemented meaningfully here..
262 int32 OsLayer::FindRegion(uint64 addr) {
263   static bool warned = false;
264
265   if (regionsize_ == 0) {
266     regionsize_ = totalmemsize_ / 8;
267     if (regionsize_ < 512 * kMegabyte)
268       regionsize_ = 512 * kMegabyte;
269     regioncount_ = totalmemsize_ / regionsize_;
270     if (regioncount_ < 1) regioncount_ = 1;
271   }
272
273   int32 region_num = addr / regionsize_;
274   if (region_num >= regioncount_) {
275     if (!warned) {
276         logprintf(0, "Log: region number %d exceeds region count %d\n",
277                   region_num, regioncount_);
278         warned = true;
279     }
280     region_num = region_num % regioncount_;
281   }
282   return region_num;
283 }
284
285 // Report which cores are associated with a given region.
286 cpu_set_t *OsLayer::FindCoreMask(int32 region) {
287   sat_assert(region >= 0);
288   region %= num_nodes_;
289   if (!cpu_sets_valid_[region]) {
290     CPU_ZERO(&cpu_sets_[region]);
291     for (int i = 0; i < num_cpus_per_node_; ++i) {
292       CPU_SET(i + region * num_cpus_per_node_, &cpu_sets_[region]);
293     }
294     cpu_sets_valid_[region] = true;
295     logprintf(5, "Log: Region %d mask 0x%s\n",
296                  region, FindCoreMaskFormat(region).c_str());
297   }
298   return &cpu_sets_[region];
299 }
300
301 // Return cores associated with a given region in hex string.
302 string OsLayer::FindCoreMaskFormat(int32 region) {
303   cpu_set_t* mask = FindCoreMask(region);
304   string format = cpuset_format(mask);
305   if (format.size() < 8)
306     format = string(8 - format.size(), '0') + format;
307   return format;
308 }
309
310 // Report an error in an easily parseable way.
311 bool OsLayer::ErrorReport(const char *part, const char *symptom, int count) {
312   time_t now = time(NULL);
313   int ttf = now - time_initialized_;
314   logprintf(0, "Report Error: %s : %s : %d : %ds\n", symptom, part, count, ttf);
315   return true;
316 }
317
318 // Read the number of hugepages out of the kernel interface in proc.
319 int64 OsLayer::FindHugePages() {
320   char buf[65] = "0";
321
322   // This is a kernel interface to query the numebr of hugepages
323   // available in the system.
324   static const char *hugepages_info_file = "/proc/sys/vm/nr_hugepages";
325   int hpfile = open(hugepages_info_file, O_RDONLY);
326
327   ssize_t bytes_read = read(hpfile, buf, 64);
328   close(hpfile);
329
330   if (bytes_read <= 0) {
331     logprintf(12, "Log: /proc/sys/vm/nr_hugepages "
332                   "read did not provide data\n");
333     return 0;
334   }
335
336   if (bytes_read == 64) {
337     logprintf(0, "Process Error: /proc/sys/vm/nr_hugepages "
338                  "is surprisingly large\n");
339     return 0;
340   }
341
342   // Add a null termintation to be string safe.
343   buf[bytes_read] = '\0';
344   // Read the page count.
345   int64 pages = strtoull(buf, NULL, 10);  // NOLINT
346
347   return pages;
348 }
349
350 int64 OsLayer::FindFreeMemSize() {
351   int64 size = 0;
352   int64 minsize = 0;
353   if (totalmemsize_ > 0)
354     return totalmemsize_;
355
356   int64 pages = sysconf(_SC_PHYS_PAGES);
357   int64 avpages = sysconf(_SC_AVPHYS_PAGES);
358   int64 pagesize = sysconf(_SC_PAGESIZE);
359   int64 physsize = pages * pagesize;
360   int64 avphyssize = avpages * pagesize;
361
362   // Assume 2MB hugepages.
363   int64 hugepagesize = FindHugePages() * 2 * kMegabyte;
364
365   if ((pages == -1) || (pagesize == -1)) {
366     logprintf(0, "Process Error: sysconf could not determine memory size.\n");
367     return 0;
368   }
369
370   // We want to leave enough stuff for things to run.
371   // If the user specified a minimum amount of memory to expect, require that.
372   // Otherwise, if more than 2GB is present, leave 192M + 5% for other stuff.
373   // If less than 2GB is present use 85% of what's available.
374   // These are fairly arbitrary numbers that seem to work OK.
375   //
376   // TODO(nsanders): is there a more correct way to determine target
377   // memory size?
378   if (hugepagesize > 0 && min_hugepages_bytes_ > 0) {
379     minsize = min_hugepages_bytes_;
380   } else if (physsize < 2048LL * kMegabyte) {
381     minsize = ((pages * 85) / 100) * pagesize;
382   } else {
383     minsize = ((pages * 95) / 100) * pagesize - (192 * kMegabyte);
384   }
385
386   // Use hugepage sizing if available.
387   if (hugepagesize > 0) {
388     if (hugepagesize < minsize) {
389       logprintf(0, "Procedural Error: Not enough hugepages. "
390                    "%lldMB available < %lldMB required.\n",
391                 hugepagesize / kMegabyte,
392                 minsize / kMegabyte);
393       // Require the calculated minimum amount of memory.
394       size = minsize;
395     } else {
396       // Require that we get all hugepages.
397       size = hugepagesize;
398     }
399   } else {
400     // Require the calculated minimum amount of memory.
401     size = minsize;
402   }
403
404   logprintf(5, "Log: Total %lld MB. Free %lld MB. Hugepages %lld MB. "
405                "Targeting %lld MB (%lld%%)\n",
406             physsize / kMegabyte,
407             avphyssize / kMegabyte,
408             hugepagesize / kMegabyte,
409             size / kMegabyte,
410             size * 100 / physsize);
411
412   totalmemsize_ = size;
413   return size;
414 }
415
416 // Allocates all memory available.
417 int64 OsLayer::AllocateAllMem() {
418   int64 length = FindFreeMemSize();
419   bool retval = AllocateTestMem(length, 0);
420   if (retval)
421     return length;
422   else
423     return 0;
424 }
425
426 // Allocate the target memory. This may be from malloc, hugepage pool
427 // or other platform specific sources.
428 bool OsLayer::AllocateTestMem(int64 length, uint64 paddr_base) {
429   // Try hugepages first.
430   void *buf = 0;
431
432   sat_assert(length >= 0);
433
434   if (paddr_base)
435     logprintf(0, "Process Error: non zero paddr_base %#llx is not supported,"
436               " ignore.\n", paddr_base);
437
438   // Determine optimal memory allocation path.
439   bool prefer_hugepages = false;
440   bool prefer_posix_shm = false;
441   bool prefer_dynamic_mapping = false;
442
443   // Are there enough hugepages?
444   int64 hugepagesize = FindHugePages() * 2 * kMegabyte;
445   // TODO(nsanders): Is there enough /dev/shm? Is there enough free memeory?
446   if ((length >= 1400LL * kMegabyte) && (address_mode_ == 32)) {
447     prefer_dynamic_mapping = true;
448     prefer_posix_shm = true;
449     logprintf(3, "Log: Prefer POSIX shared memory allocation.\n");
450     logprintf(3, "Log: You may need to run "
451                  "'sudo mount -o remount,size=100\% /dev/shm.'\n");
452   } else if (hugepagesize >= length) {
453     prefer_hugepages = true;
454     logprintf(3, "Log: Prefer using hugepace allocation.\n");
455   } else {
456     logprintf(3, "Log: Prefer plain malloc memory allocation.\n");
457   }
458
459 #ifdef HAVE_SYS_SHM_H
460   // Allocate hugepage mapped memory.
461   if (prefer_hugepages) {
462     do { // Allow break statement.
463       int shmid;
464       void *shmaddr;
465
466       if ((shmid = shmget(2, length,
467               SHM_HUGETLB | IPC_CREAT | SHM_R | SHM_W)) < 0) {
468         int err = errno;
469         string errtxt = ErrorString(err);
470         logprintf(3, "Log: failed to allocate shared hugepage "
471                       "object - err %d (%s)\n",
472                   err, errtxt.c_str());
473         logprintf(3, "Log: sysctl -w vm.nr_hugepages=XXX allows hugepages.\n");
474         break;
475       }
476
477       shmaddr = shmat(shmid, NULL, NULL);
478       if (shmaddr == reinterpret_cast<void*>(-1)) {
479         int err = errno;
480         string errtxt = ErrorString(err);
481         logprintf(0, "Log: failed to attach shared "
482                      "hugepage object - err %d (%s).\n",
483                   err, errtxt.c_str());
484         if (shmctl(shmid, IPC_RMID, NULL) < 0) {
485           int err = errno;
486           string errtxt = ErrorString(err);
487           logprintf(0, "Log: failed to remove shared "
488                        "hugepage object - err %d (%s).\n",
489                     err, errtxt.c_str());
490         }
491         break;
492       }
493       use_hugepages_ = true;
494       shmid_ = shmid;
495       buf = shmaddr;
496       logprintf(0, "Log: Using shared hugepage object 0x%x at %p.\n",
497                 shmid, shmaddr);
498     } while (0);
499   }
500
501   if ((!use_hugepages_) && prefer_posix_shm) {
502     do {
503       int shm_object;
504       void *shmaddr = NULL;
505
506       shm_object = shm_open("/stressapptest", O_CREAT | O_RDWR, S_IRWXU);
507       if (shm_object < 0) {
508         int err = errno;
509         string errtxt = ErrorString(err);
510         logprintf(3, "Log: failed to allocate shared "
511                       "smallpage object - err %d (%s)\n",
512                   err, errtxt.c_str());
513         break;
514       }
515
516       if (0 > ftruncate(shm_object, length)) {
517         int err = errno;
518         string errtxt = ErrorString(err);
519         logprintf(3, "Log: failed to ftruncate shared "
520                       "smallpage object - err %d (%s)\n",
521                   err, errtxt.c_str());
522         break;
523       }
524
525       // 32 bit linux apps can only use ~1.4G of address space.
526       // Use dynamic mapping for allocations larger than that.
527       // Currently perf hit is ~10% for this.
528       if (prefer_dynamic_mapping) {
529         dynamic_mapped_shmem_ = true;
530       } else {
531         // Do a full mapping here otherwise.
532         shmaddr = mmap64(NULL, length, PROT_READ | PROT_WRITE,
533                          MAP_SHARED | MAP_NORESERVE | MAP_LOCKED | MAP_POPULATE,
534                          shm_object, NULL);
535         if (shmaddr == reinterpret_cast<void*>(-1)) {
536           int err = errno;
537           string errtxt = ErrorString(err);
538           logprintf(0, "Log: failed to map shared "
539                        "smallpage object - err %d (%s).\n",
540                     err, errtxt.c_str());
541           break;
542         }
543       }
544
545       use_posix_shm_ = true;
546       shmid_ = shm_object;
547       buf = shmaddr;
548       char location_message[256] = "";
549       if (dynamic_mapped_shmem_) {
550         sprintf(location_message, "mapped as needed");
551       } else {
552         sprintf(location_message, "at %p", shmaddr);
553       }
554       logprintf(0, "Log: Using posix shared memory object 0x%x %s.\n",
555                 shm_object, location_message);
556     } while (0);
557     shm_unlink("/stressapptest");
558   }
559 #endif // HAVE_SYS_SHM_H
560
561   if (!use_hugepages_ && !use_posix_shm_) {
562     // Use memalign to ensure that blocks are aligned enough for disk direct IO.
563     buf = static_cast<char*>(memalign(4096, length));
564     if (buf) {
565       logprintf(0, "Log: Using memaligned allocation at %p.\n", buf);
566     } else {
567       logprintf(0, "Process Error: memalign returned 0\n");
568       if ((length >= 1499LL * kMegabyte) && (address_mode_ == 32)) {
569         logprintf(0, "Log: You are trying to allocate > 1.4G on a 32 "
570                      "bit process. Please setup shared memory.\n");
571       }
572     }
573   }
574
575   testmem_ = buf;
576   if (buf || dynamic_mapped_shmem_) {
577     testmemsize_ = length;
578   } else {
579     testmemsize_ = 0;
580   }
581
582   return (buf != 0) || dynamic_mapped_shmem_;
583 }
584
585 // Free the test memory.
586 void OsLayer::FreeTestMem() {
587   if (testmem_) {
588     if (use_hugepages_) {
589 #ifdef HAVE_SYS_SHM_H
590       shmdt(testmem_);
591       shmctl(shmid_, IPC_RMID, NULL);
592 #endif
593     } else if (use_posix_shm_) {
594       if (!dynamic_mapped_shmem_) {
595         munmap(testmem_, testmemsize_);
596       }
597       close(shmid_);
598     } else {
599       free(testmem_);
600     }
601     testmem_ = 0;
602     testmemsize_ = 0;
603   }
604 }
605
606
607 // Prepare the target memory. It may requre mapping in, or this may be a noop.
608 void *OsLayer::PrepareTestMem(uint64 offset, uint64 length) {
609   sat_assert((offset + length) <= testmemsize_);
610   if (dynamic_mapped_shmem_) {
611     // TODO(nsanders): Check if we can support MAP_NONBLOCK,
612     // and evaluate performance hit from not using it.
613 #ifdef HAVE_MMAP64
614     void * mapping = mmap64(NULL, length, PROT_READ | PROT_WRITE,
615                      MAP_SHARED | MAP_NORESERVE | MAP_LOCKED | MAP_POPULATE,
616                      shmid_, offset);
617 #else
618     void * mapping = mmap(NULL, length, PROT_READ | PROT_WRITE,
619                      MAP_SHARED | MAP_NORESERVE | MAP_LOCKED | MAP_POPULATE,
620                      shmid_, offset);
621 #endif
622     if (mapping == MAP_FAILED) {
623       string errtxt = ErrorString(errno);
624       logprintf(0, "Process Error: PrepareTestMem mmap64(%llx, %llx) failed. "
625                    "error: %s.\n",
626                 offset, length, errtxt.c_str());
627       sat_assert(0);
628     }
629     return mapping;
630   }
631
632   return reinterpret_cast<void*>(reinterpret_cast<char*>(testmem_) + offset);
633 }
634
635 // Release the test memory resources, if any.
636 void OsLayer::ReleaseTestMem(void *addr, uint64 offset, uint64 length) {
637   if (dynamic_mapped_shmem_) {
638     int retval = munmap(addr, length);
639     if (retval == -1) {
640       string errtxt = ErrorString(errno);
641       logprintf(0, "Process Error: ReleaseTestMem munmap(%p, %llx) failed. "
642                    "error: %s.\n",
643                 addr, length, errtxt.c_str());
644       sat_assert(0);
645     }
646   }
647 }
648
649 // No error polling on unknown systems.
650 int OsLayer::ErrorPoll() {
651   return 0;
652 }
653
654 // Generally, poll for errors once per second.
655 void OsLayer::ErrorWait() {
656   sat_sleep(1);
657   return;
658 }
659
660 // Open a PCI bus-dev-func as a file and return its file descriptor.
661 // Error is indicated by return value less than zero.
662 int OsLayer::PciOpen(int bus, int device, int function) {
663   char dev_file[256];
664
665   snprintf(dev_file, sizeof(dev_file), "/proc/bus/pci/%02x/%02x.%x",
666            bus, device, function);
667
668   int fd = open(dev_file, O_RDWR);
669   if (fd == -1) {
670     logprintf(0, "Process Error: Unable to open PCI bus %d, device %d, "
671                  "function %d (errno %d).\n",
672               bus, device, function, errno);
673     return -1;
674   }
675
676   return fd;
677 }
678
679
680 // Read and write functions to access PCI config.
681 uint32 OsLayer::PciRead(int fd, uint32 offset, int width) {
682   // Strict aliasing rules lawyers will cause data corruption
683   // on cast pointers in some gccs.
684   union {
685     uint32 l32;
686     uint16 l16;
687     uint8 l8;
688   } datacast;
689   datacast.l32 = 0;
690   uint32 size = width / 8;
691
692   sat_assert((width == 32) || (width == 16) || (width == 8));
693   sat_assert(offset <= (256 - size));
694
695   if (lseek(fd, offset, SEEK_SET) < 0) {
696     logprintf(0, "Process Error: Can't seek %x\n", offset);
697     return 0;
698   }
699   if (read(fd, &datacast, size) != static_cast<ssize_t>(size)) {
700     logprintf(0, "Process Error: Can't read %x\n", offset);
701     return 0;
702   }
703
704   // Extract the data.
705   switch (width) {
706     case 8:
707       sat_assert(&(datacast.l8) == reinterpret_cast<uint8*>(&datacast));
708       return datacast.l8;
709     case 16:
710       sat_assert(&(datacast.l16) == reinterpret_cast<uint16*>(&datacast));
711       return datacast.l16;
712     case 32:
713       return datacast.l32;
714   }
715   return 0;
716 }
717
718 void OsLayer::PciWrite(int fd, uint32 offset, uint32 value, int width) {
719   // Strict aliasing rules lawyers will cause data corruption
720   // on cast pointers in some gccs.
721   union {
722     uint32 l32;
723     uint16 l16;
724     uint8 l8;
725   } datacast;
726   datacast.l32 = 0;
727   uint32 size = width / 8;
728
729   sat_assert((width == 32) || (width == 16) || (width == 8));
730   sat_assert(offset <= (256 - size));
731
732   // Cram the data into the right alignment.
733   switch (width) {
734     case 8:
735       sat_assert(&(datacast.l8) == reinterpret_cast<uint8*>(&datacast));
736       datacast.l8 = value;
737     case 16:
738       sat_assert(&(datacast.l16) == reinterpret_cast<uint16*>(&datacast));
739       datacast.l16 = value;
740     case 32:
741       datacast.l32 = value;
742   }
743
744   if (lseek(fd, offset, SEEK_SET) < 0) {
745     logprintf(0, "Process Error: Can't seek %x\n", offset);
746     return;
747   }
748   if (write(fd, &datacast, size) != static_cast<ssize_t>(size)) {
749     logprintf(0, "Process Error: Can't write %x to %x\n", datacast.l32, offset);
750     return;
751   }
752
753   return;
754 }
755
756
757
758 // Open dev msr.
759 int OsLayer::OpenMSR(uint32 core, uint32 address) {
760   char buf[256];
761   snprintf(buf, sizeof(buf), "/dev/cpu/%d/msr", core);
762   int fd = open(buf, O_RDWR);
763   if (fd < 0)
764     return fd;
765
766   uint32 pos = lseek(fd, address, SEEK_SET);
767   if (pos != address) {
768     close(fd);
769     logprintf(5, "Log: can't seek to msr %x, cpu %d\n", address, core);
770     return -1;
771   }
772
773   return fd;
774 }
775
776 bool OsLayer::ReadMSR(uint32 core, uint32 address, uint64 *data) {
777   int fd = OpenMSR(core, address);
778   if (fd < 0)
779     return false;
780
781   // Read from the msr.
782   bool res = (sizeof(*data) == read(fd, data, sizeof(*data)));
783
784   if (!res)
785     logprintf(5, "Log: Failed to read msr %x core %d\n", address, core);
786
787   close(fd);
788
789   return res;
790 }
791
792 bool OsLayer::WriteMSR(uint32 core, uint32 address, uint64 *data) {
793   int fd = OpenMSR(core, address);
794   if (fd < 0)
795     return false;
796
797   // Write to the msr
798   bool res = (sizeof(*data) == write(fd, data, sizeof(*data)));
799
800   if (!res)
801     logprintf(5, "Log: Failed to write msr %x core %d\n", address, core);
802
803   close(fd);
804
805   return res;
806 }
807
808 // Extract bits [n+len-1, n] from a 32 bit word.
809 // so GetBitField(0x0f00, 8, 4) == 0xf.
810 uint32 OsLayer::GetBitField(uint32 val, uint32 n, uint32 len) {
811   return (val >> n) & ((1<<len) - 1);
812 }
813
814 // Generic CPU stress workload that would work on any CPU/Platform.
815 // Float-point array moving average calculation.
816 bool OsLayer::CpuStressWorkload() {
817   double float_arr[100];
818   double sum = 0;
819   unsigned int seed = 12345;
820
821   // Initialize array with random numbers.
822   for (int i = 0; i < 100; i++) {
823 #ifdef HAVE_RAND_R
824     float_arr[i] = rand_r(&seed);
825     if (rand_r(&seed) % 2)
826       float_arr[i] *= -1.0;
827 #else
828     float_arr[i] = rand();
829     if (rand() % 2)
830       float_arr[i] *= -1.0;
831 #endif
832   }
833
834   // Calculate moving average.
835   for (int i = 0; i < 100000000; i++) {
836     float_arr[i % 100] =
837       (float_arr[i % 100] + float_arr[(i + 1) % 100] +
838        float_arr[(i + 99) % 100]) / 3;
839     sum += float_arr[i % 100];
840   }
841
842   // Artificial printf so the loops do not get optimized away.
843   if (sum == 0.0)
844     logprintf(12, "Log: I'm Feeling Lucky!\n");
845   return true;
846 }
847
848 PCIDevices OsLayer::GetPCIDevices() {
849   PCIDevices device_list;
850   DIR *dir;
851   struct dirent *buf = new struct dirent();
852   struct dirent *entry;
853   dir = opendir(kSysfsPath);
854   if (!dir)
855     logprintf(0, "Process Error: Cannot open %s", kSysfsPath);
856   while (readdir_r(dir, buf, &entry) == 0 && entry) {
857     PCIDevice *device;
858     unsigned int dev, func;
859     // ".", ".." or a special non-device perhaps.
860     if (entry->d_name[0] == '.')
861       continue;
862
863     device = new PCIDevice();
864     if (sscanf(entry->d_name, "%04x:%02hx:%02x.%d",
865                &device->domain, &device->bus, &dev, &func) < 4) {
866       logprintf(0, "Process Error: Couldn't parse %s", entry->d_name);
867       free(device);
868       continue;
869     }
870     device->dev = dev;
871     device->func = func;
872     device->vendor_id = PCIGetValue(entry->d_name, "vendor");
873     device->device_id = PCIGetValue(entry->d_name, "device");
874     PCIGetResources(entry->d_name, device);
875     device_list.insert(device_list.end(), device);
876   }
877   closedir(dir);
878   delete buf;
879   return device_list;
880 }
881
882 int OsLayer::PCIGetValue(string name, string object) {
883   int fd, len;
884   char filename[256];
885   char buf[256];
886   snprintf(filename, sizeof(filename), "%s/%s/%s", kSysfsPath,
887            name.c_str(), object.c_str());
888   fd = open(filename, O_RDONLY);
889   if (fd < 0)
890     return 0;
891   len = read(fd, buf, 256);
892   close(fd);
893   buf[len] = '\0';
894   return strtol(buf, NULL, 0);  // NOLINT
895 }
896
897 int OsLayer::PCIGetResources(string name, PCIDevice *device) {
898   char filename[256];
899   char buf[256];
900   FILE *file;
901   int64 start;
902   int64 end;
903   int64 size;
904   int i;
905   snprintf(filename, sizeof(filename), "%s/%s/%s", kSysfsPath,
906            name.c_str(), "resource");
907   file = fopen(filename, "r");
908   if (!file) {
909     logprintf(0, "Process Error: impossible to find resource file for %s",
910               filename);
911     return errno;
912   }
913   for (i = 0; i < 6; i++) {
914     if (!fgets(buf, 256, file))
915       break;
916     sscanf(buf, "%llx %llx", &start, &end);  // NOLINT
917     size = 0;
918     if (start)
919       size = end - start + 1;
920     device->base_addr[i] = start;
921     device->size[i] = size;
922   }
923   fclose(file);
924   return 0;
925 }