/src/UserSpaceInstrumentation/RegisterState.cpp

https://github.com/pierricgimmig/orbit · C++ · 147 lines · 109 code · 24 blank · 14 comment · 27 complexity · d8778a2db0cd9675db9ef1bffb6cfcf0 MD5 · raw file

  1. // Copyright (c) 2021 The Orbit 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. #include "RegisterState.h"
  5. #include <cpuid.h>
  6. #include <elf.h>
  7. #include <errno.h>
  8. #include <sys/ptrace.h>
  9. #include <sys/uio.h>
  10. #include <string>
  11. #include "OrbitBase/Logging.h"
  12. #include "OrbitBase/SafeStrerror.h"
  13. #include "absl/strings/str_format.h"
  14. namespace orbit_user_space_instrumentation {
  15. namespace {
  16. // Some notes reguarding the calls to cpuid below:
  17. // Cpuid can be used to query all sorts of information about the cpu (presence of features,
  18. // specifications, ...). Cpuid takes one parameter in eax. In Intel's terminology, this is called
  19. // the Cpuid "leaf". Some leaves have "sub-leafs" i.e. they take a second paramter in ecx. The
  20. // sub-leaf is sometimes called "count". The return values end up in registers eax, ... , edx. The
  21. // wrappers from cpuid.h simplify error and parameter handling. cpuid.h also has some defines and
  22. // useful comments to figure out what can be queried. More comprehensive info:
  23. // https://www.sandpile.org/x86/cpuid.htm
  24. // Return the size of the XSave area on this cpu.
  25. [[nodiscard]] ErrorMessageOr<size_t> GetXSaveAreaSize() {
  26. uint32_t eax = 0;
  27. uint32_t ebx = 0;
  28. uint32_t ecx = 0;
  29. uint32_t edx = 0;
  30. if (!__get_cpuid(0x01, &eax, &ebx, &ecx, &edx) || !(ecx & bit_XSAVE)) {
  31. return ErrorMessage("XSAVE is not supported by the Cpu.");
  32. }
  33. if (!__get_cpuid_count(0x0d, 0x00, &eax, &ebx, &ecx, &edx)) {
  34. return ErrorMessage("XSAVE is not supported by the Cpu.");
  35. }
  36. return static_cast<size_t>(ecx);
  37. }
  38. // Return offset of the YMMx registers inside the extended section of the XSave area.
  39. [[nodiscard]] ErrorMessageOr<size_t> GetAvxOffset() {
  40. uint32_t eax = 0;
  41. uint32_t ebx = 0;
  42. uint32_t ecx = 0;
  43. uint32_t edx = 0;
  44. if (!__get_cpuid(0x01, &eax, &ebx, &ecx, &edx) || !(ecx & bit_AVX)) {
  45. return ErrorMessage("AVX is not supported by the Cpu.");
  46. }
  47. if (!__get_cpuid_count(0x0d, 0x02, &eax, &ebx, &ecx, &edx)) {
  48. return ErrorMessage("AVX offset query failed.");
  49. }
  50. return static_cast<size_t>(ebx);
  51. }
  52. } // namespace
  53. bool RegisterState::Hasx87DataStored() {
  54. return (static_cast<uint64_t>(GetXSaveHeader()->xstate_bv) &
  55. static_cast<uint64_t>(XSaveHeader::StateComponents::kX87)) != 0;
  56. }
  57. bool RegisterState::HasSseDataStored() {
  58. return (static_cast<uint64_t>(GetXSaveHeader()->xstate_bv) &
  59. static_cast<uint64_t>(XSaveHeader::StateComponents::kSse)) != 0;
  60. }
  61. bool RegisterState::HasAvxDataStored() {
  62. return (static_cast<uint64_t>(GetXSaveHeader()->xstate_bv) &
  63. static_cast<uint64_t>(XSaveHeader::StateComponents::kAvx)) != 0;
  64. }
  65. ErrorMessageOr<void> RegisterState::BackupRegisters(pid_t tid) {
  66. tid_ = tid;
  67. iovec iov;
  68. iov.iov_base = &general_purpose_registers_;
  69. iov.iov_len = sizeof(GeneralPurposeRegisters);
  70. auto result = ptrace(PTRACE_GETREGSET, tid, NT_PRSTATUS, &iov);
  71. if (result == -1) {
  72. return ErrorMessage(absl::StrFormat("PTRACE_GETREGS, NT_PRSTATUS failed with errno: %d: %s",
  73. errno, SafeStrerror(errno)));
  74. }
  75. if (iov.iov_len == sizeof(GeneralPurposeRegisters32)) {
  76. bitness_ = Bitness::k32Bit;
  77. } else if (iov.iov_len == sizeof(GeneralPurposeRegisters64)) {
  78. bitness_ = Bitness::k64Bit;
  79. } else {
  80. ORBIT_FATAL("Bitness is neither 32 or 64 bit.");
  81. }
  82. auto xsave_area_size = GetXSaveAreaSize();
  83. if (xsave_area_size.has_error()) {
  84. return xsave_area_size.error();
  85. }
  86. xsave_area_.resize(xsave_area_size.value());
  87. iov.iov_len = xsave_area_.size();
  88. iov.iov_base = xsave_area_.data();
  89. result = ptrace(PTRACE_GETREGSET, tid, NT_X86_XSTATE, &iov);
  90. if (result == -1) {
  91. return ErrorMessage(absl::StrFormat("PTRACE_GETREGS, NT_X86_XSTATE failed with errno: %d: %s",
  92. errno, SafeStrerror(errno)));
  93. }
  94. auto avx_offset = GetAvxOffset();
  95. if (!avx_offset.has_error()) {
  96. avx_offset_ = avx_offset.value();
  97. }
  98. return outcome::success();
  99. }
  100. ErrorMessageOr<void> RegisterState::RestoreRegisters() {
  101. // BackupRegisters needs to be called before RestoreRegisters.
  102. ORBIT_CHECK(tid_ != -1);
  103. iovec iov;
  104. iov.iov_base = &general_purpose_registers_;
  105. iov.iov_len = (bitness_ == Bitness::k32Bit) ? sizeof(GeneralPurposeRegisters32)
  106. : sizeof(GeneralPurposeRegisters64);
  107. auto result = ptrace(PTRACE_SETREGSET, tid_, NT_PRSTATUS, &iov);
  108. if (result == -1) {
  109. return ErrorMessage(
  110. absl::StrFormat("PTRACE_SETREGSET failed to write NT_PRSTATUS with errno: %d: %s", errno,
  111. SafeStrerror(errno)));
  112. }
  113. iov.iov_len = xsave_area_.size();
  114. iov.iov_base = xsave_area_.data();
  115. result = ptrace(PTRACE_SETREGSET, tid_, NT_X86_XSTATE, &iov);
  116. if (result == -1) {
  117. return ErrorMessage(
  118. absl::StrFormat("PTRACE_SETREGSET failed to write NT_X86_XSTATE with errno: %d: %s", errno,
  119. SafeStrerror(errno)));
  120. }
  121. return outcome::success();
  122. }
  123. } // namespace orbit_user_space_instrumentation