<commit_before>// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "google/cloud/iam/iam_credentials_client.h"
#include "google/cloud/spanner/instance_admin_client.h"
#include "google/cloud/internal/getenv.h"
#include "google/cloud/log.h"
#include "google/cloud/testing_util/example_driver.h"
#include "absl/strings/str_split.h"
#include "absl/time/time.h"  // NOLINT(modernize-deprecated-headers)
#include <curl/curl.h>
#include <hello_world_grpc/hello_world.grpc.pb.h>
#include <chrono>
#include <stdexcept>
#include <thread>

namespace {

auto constexpr kTokenValidationPeriod = std::chrono::seconds(30);

extern "C" size_t CurlOnWriteData(char* ptr, size_t size, size_t nmemb,
                                  void* userdata) {
  auto* buffer = reinterpret_cast<std::string*>(userdata);
  buffer->append(ptr, size * nmemb);
  return size * nmemb;
}

google::cloud::StatusOr<std::string> HttpGet(std::string const& url,
                                             std::string const& token) {
  static auto const kCurlInit = [] {
    return curl_global_init(CURL_GLOBAL_ALL);
  }();
  (void)kCurlInit;
  auto const authorization = "Authorization: Bearer " + token;
  using Headers = std::unique_ptr<curl_slist, decltype(&curl_slist_free_all)>;
  auto const headers = Headers{
      curl_slist_append(nullptr, authorization.c_str()), curl_slist_free_all};
  using CurlHandle = std::unique_ptr<CURL, decltype(&curl_easy_cleanup)>;
  auto curl = CurlHandle(curl_easy_init(), curl_easy_cleanup);
  if (!curl) throw std::runtime_error("Failed to create CurlHandle");
  std::string buffer;
  curl_easy_setopt(curl.get(), CURLOPT_URL, url.c_str());
  curl_easy_setopt(curl.get(), CURLOPT_HTTPHEADER, headers.get());
  curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, &CurlOnWriteData);
  curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, &buffer);

  CURLcode code = curl_easy_perform(curl.get());
  if (code != CURLE_OK) throw std::runtime_error(curl_easy_strerror(code));
  long status;  // NOLINT(google-runtime-int)
  code = curl_easy_getinfo(curl.get(), CURLINFO_RESPONSE_CODE, &status);
  if (code != CURLE_OK) throw std::runtime_error(curl_easy_strerror(code));

  // Handle common errors as Status, this is not exhaustive.
  using ::google::cloud::Status;
  using ::google::cloud::StatusCode;
  if (status == 400) return Status(StatusCode::kInvalidArgument, buffer);
  if (status == 401) return Status(StatusCode::kUnauthenticated, buffer);
  if (status == 403) return Status(StatusCode::kPermissionDenied, buffer);
  if (status >= 500) return Status(StatusCode::kInternal, buffer);
  if (status < 200 || status >= 300) {
    std::ostringstream os;
    os << "HTTP error [" << status << "]: " << buffer;
    return Status(StatusCode::kUnknown, buffer);
  }
  return buffer;
}

// TODO(#6185) - this should be done by the generated code
std::set<std::string> DefaultTracingComponents() {
  return absl::StrSplit(
      google::cloud::internal::GetEnv("GOOGLE_CLOUD_CPP_ENABLE_TRACING")
          .value_or(""),
      ',');
}

google::iam::credentials::v1::GenerateAccessTokenResponse UseAccessToken(
    google::cloud::iam::IAMCredentialsClient client,
    std::vector<std::string> const& argv) {
  namespace iam = google::cloud::iam;
  return [](iam::IAMCredentialsClient client,
            std::string const& service_account, std::string const& project_id) {
    google::protobuf::Duration duration;
    duration.set_seconds(
        std::chrono::seconds(2 * kTokenValidationPeriod).count());
    auto token = client.GenerateAccessToken(
        "projects/-/serviceAccounts/" + service_account, /*delegates=*/{},
        /*scope=*/{"https://www.googleapis.com/auth/cloud-platform"}, duration);
    if (!token) throw std::runtime_error(token.status().message());

    namespace spanner = google::cloud::spanner;
    auto credentials = grpc::CompositeChannelCredentials(
        grpc::SslCredentials({}),
        grpc::AccessTokenCredentials(token->access_token()));

    spanner::InstanceAdminClient admin(spanner::MakeInstanceAdminConnection(
        google::cloud::Options{}.set<google::cloud::GrpcCredentialOption>(
            credentials)));
    for (auto instance : admin.ListInstances(project_id, /*filter=*/{})) {
      if (!instance) throw std::runtime_error(instance.status().message());
      std::cout << "Instance: " << instance->name() << "\n";
    }

    return *std::move(token);
  }(std::move(client), argv.at(0), argv.at(1));
}

void UseAccessTokenUntilExpired(google::cloud::iam::IAMCredentialsClient client,
                                std::vector<std::string> const& argv) {
  auto token = UseAccessToken(std::move(client), argv);
  auto const project_id = argv.at(1);
  auto const expiration =
      std::chrono::system_clock::from_time_t(token.expire_time().seconds());
  auto const deadline = expiration + 4 * kTokenValidationPeriod;
  std::cout << "Running until " << absl::FromChrono(deadline)
            << ". This is past the access token expiration time ("
            << absl::FromChrono(expiration) << ")\n";

  auto iteration = [=](bool expired) {
    namespace spanner = google::cloud::spanner;
    auto credentials = grpc::CompositeChannelCredentials(
        grpc::SslCredentials({}),
        grpc::AccessTokenCredentials(token.access_token()));
    spanner::InstanceAdminClient admin(spanner::MakeInstanceAdminConnection(
        google::cloud::Options{}.set<google::cloud::GrpcCredentialOption>(
            credentials)));
    for (auto instance : admin.ListInstances(project_id, /*filter=*/{})) {
      // kUnauthenticated receives special treatment, it is the error received
      // when the token expires.
      if (instance.status().code() ==
          google::cloud::StatusCode::kUnauthenticated) {
        std::cout << "error [" << instance.status() << "]";
        if (!expired) {
          std::cout << ": unexpected, but could be a race condition."
                    << " Trying again\n";
          return true;
        }
        std::cout << ": this is expected as the token is expired\n";
        return false;
      }
      if (!instance) throw std::runtime_error(instance.status().message());
      std::cout << "success (" << instance->name() << ")\n";
      return true;
    }
    return false;
  };

  for (auto now = std::chrono::system_clock::now(); now < deadline;
       now = std::chrono::system_clock::now()) {
    auto const expired = (now > expiration);
    std::cout << absl::FromChrono(now) << ": running iteration with "
              << (expired ? "an expired" : "a valid") << " token ";
    if (!iteration(expired)) break;
    std::this_thread::sleep_for(kTokenValidationPeriod);
  }
}

void UseIdTokenHttp(google::cloud::iam::IAMCredentialsClient client,
                    std::vector<std::string> const& argv) {
  namespace iam = google::cloud::iam;
  [](iam::IAMCredentialsClient client, std::string const& service_account,
     std::string const& hello_world_url) {
    auto token = client.GenerateIdToken(
        "projects/-/serviceAccounts/" + service_account, /*delegates=*/{},
        /*audience=*/{hello_world_url},
        /*include_email=*/true);
    if (!token) throw std::runtime_error(token.status().message());

    auto backoff = std::chrono::milliseconds(250);
    for (int i = 0; i != 3; ++i) {
      auto text = HttpGet(hello_world_url, token->token());
      if (text.ok()) {
        std::cout << "Server says: " << *text << "\n";
        return;
      }
      std::this_thread::sleep_for(backoff);
      backoff *= 2;
    }
    throw std::runtime_error("Could not contact server after 3 attempts");
  }(std::move(client), argv.at(0), argv.at(1));
}

void UseIdTokenGrpc(google::cloud::iam::IAMCredentialsClient client,
                    std::vector<std::string> const& argv) {
  namespace iam = google::cloud::iam;
  [](iam::IAMCredentialsClient client, std::string const& service_account,
     std::string const& url) {
    auto token = client.GenerateIdToken(
        "projects/-/serviceAccounts/" + service_account, /*delegates=*/{},
        /*audience=*/{url},
        /*include_email=*/true);
    if (!token) throw std::runtime_error(token.status().message());

    auto const prefix = std::string{"https://"};
    if (url.rfind(prefix, 0) != 0) {
      throw std::runtime_error("Invalid URL" + url);
    }
    auto endpoint = url.substr(prefix.length()) + ":443";
    auto credentials = grpc::CompositeChannelCredentials(
        grpc::SslCredentials(grpc::SslCredentialsOptions{}),
        grpc::AccessTokenCredentials(token->token()));
    auto channel = grpc::CreateChannel(endpoint, credentials);
    auto stub = google::cloud::examples::Greet::NewStub(channel);
    auto request = google::cloud::examples::HelloRequest{};
    auto backoff = std::chrono::milliseconds(250);
    for (int i = 0; i != 3; ++i) {
      grpc::ClientContext context;
      google::cloud::examples::HelloResponse response;
      auto status = stub->Hello(&context, request, &response);
      if (status.ok()) {
        std::cout << "Servers says: " << response.greeting() << "\n";
        return;
      }
      std::cout << "Server returned error=" << status.error_code()
                << ", message=" << status.error_message() << "\n";
      std::this_thread::sleep_for(backoff);
      backoff *= 2;
    }
    throw std::runtime_error("Could not contact server after 3 attempts");
  }(std::move(client), argv.at(0), argv.at(1));
}

void AutoRun(std::vector<std::string> const& argv) {
  namespace examples = ::google::cloud::testing_util;
  using google::cloud::internal::GetEnv;

  if (!argv.empty()) throw examples::Usage{"auto"};
  examples::CheckEnvironmentVariablesAreSet({
      "GOOGLE_CLOUD_PROJECT",
      "GOOGLE_CLOUD_CPP_IAM_TEST_SERVICE_ACCOUNT",
      "GOOGLE_CLOUD_CPP_TEST_HELLO_WORLD_SERVICE_ACCOUNT",
      "GOOGLE_CLOUD_CPP_TEST_HELLO_WORLD_HTTP_URL",
      "GOOGLE_CLOUD_CPP_TEST_HELLO_WORLD_GRPC_URL",
  });
  auto project_id = GetEnv("GOOGLE_CLOUD_PROJECT").value();
  auto const test_iam_service_account =
      GetEnv("GOOGLE_CLOUD_CPP_IAM_TEST_SERVICE_ACCOUNT").value_or("");
  auto const hello_world_service_account =
      GetEnv("GOOGLE_CLOUD_CPP_TEST_HELLO_WORLD_SERVICE_ACCOUNT").value_or("");
  auto const hello_world_http_url =
      GetEnv("GOOGLE_CLOUD_CPP_TEST_HELLO_WORLD_HTTP_URL").value_or("");
  auto const hello_world_grpc_url =
      GetEnv("GOOGLE_CLOUD_CPP_TEST_HELLO_WORLD_GRPC_URL").value_or("");

  auto client = google::cloud::iam::IAMCredentialsClient(
      google::cloud::iam::MakeIAMCredentialsConnection(
          google::cloud::Options{}.set<google::cloud::TracingComponentsOption>(
              DefaultTracingComponents())));

  std::cout << "\nRunning UseAccessToken() example" << std::endl;
  UseAccessToken(client, {test_iam_service_account, project_id});

  std::cout << "\nRunning UseAccessTokenUntilExpired() example" << std::endl;
  UseAccessTokenUntilExpired(client, {test_iam_service_account, project_id});

  std::cout << "\nRunning UseIdTokenHttp() example" << std::endl;
  UseIdTokenHttp(client, {hello_world_service_account, hello_world_http_url});

  std::cout << "\nRunning UseIdTokenGrpc() example" << std::endl;
  UseIdTokenGrpc(client, {hello_world_service_account, hello_world_grpc_url});
}

}  // namespace

int main(int argc, char* argv[]) {  // NOLINT(bugprone-exception-escape)
  using ::google::cloud::testing_util::Example;

  using ClientCommand = std::function<void(
      google::cloud::iam::IAMCredentialsClient, std::vector<std::string> argv)>;

  auto make_entry = [](std::string name,
                       std::vector<std::string> const& arg_names,
                       ClientCommand const& command) {
    auto adapter = [=](std::vector<std::string> argv) {
      if ((argv.size() == 1 && argv[0] == "--help") ||
          argv.size() != arg_names.size()) {
        std::string usage = name;
        for (auto const& a : arg_names) usage += " <" + a + ">";
        throw google::cloud::testing_util::Usage{std::move(usage)};
      }
      auto client = google::cloud::iam::IAMCredentialsClient(
          google::cloud::iam::MakeIAMCredentialsConnection(
              google::cloud::Options{}
                  .set<google::cloud::TracingComponentsOption>(
                      DefaultTracingComponents())));
      command(client, std::move(argv));
    };
    return google::cloud::testing_util::Commands::value_type(std::move(name),
                                                             adapter);
  };

  Example example({
      make_entry("use-access-token", {"service-account", "project-id"},
                 UseAccessToken),
      make_entry("use-access-token-until-expired",
                 {"service-account", "project-id"}, UseAccessTokenUntilExpired),
      make_entry("use-id-token-http",
                 {"service-account", "hello-world-http-url"}, UseIdTokenHttp),
      make_entry("use-id-token-grpc",
                 {"service-account", "hello-world-http-url"}, UseIdTokenGrpc),
      {"auto", AutoRun},
  });
  return example.Run(argc, argv);
}
<commit_msg>test: improve output for troubleshooting (#6845)<commit_after>// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "google/cloud/iam/iam_credentials_client.h"
#include "google/cloud/spanner/instance_admin_client.h"
#include "google/cloud/internal/getenv.h"
#include "google/cloud/log.h"
#include "google/cloud/testing_util/example_driver.h"
#include "absl/strings/str_split.h"
#include "absl/time/time.h"  // NOLINT(modernize-deprecated-headers)
#include <curl/curl.h>
#include <hello_world_grpc/hello_world.grpc.pb.h>
#include <chrono>
#include <stdexcept>
#include <thread>

namespace {

auto constexpr kTokenValidationPeriod = std::chrono::seconds(30);

extern "C" size_t CurlOnWriteData(char* ptr, size_t size, size_t nmemb,
                                  void* userdata) {
  auto* buffer = reinterpret_cast<std::string*>(userdata);
  buffer->append(ptr, size * nmemb);
  return size * nmemb;
}

google::cloud::StatusOr<std::string> HttpGet(std::string const& url,
                                             std::string const& token) {
  static auto const kCurlInit = [] {
    return curl_global_init(CURL_GLOBAL_ALL);
  }();
  (void)kCurlInit;
  auto const authorization = "Authorization: Bearer " + token;
  using Headers = std::unique_ptr<curl_slist, decltype(&curl_slist_free_all)>;
  auto const headers = Headers{
      curl_slist_append(nullptr, authorization.c_str()), curl_slist_free_all};
  using CurlHandle = std::unique_ptr<CURL, decltype(&curl_easy_cleanup)>;
  auto curl = CurlHandle(curl_easy_init(), curl_easy_cleanup);
  if (!curl) throw std::runtime_error("Failed to create CurlHandle");
  std::string buffer;
  curl_easy_setopt(curl.get(), CURLOPT_URL, url.c_str());
  curl_easy_setopt(curl.get(), CURLOPT_HTTPHEADER, headers.get());
  curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, &CurlOnWriteData);
  curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, &buffer);

  CURLcode code = curl_easy_perform(curl.get());
  if (code != CURLE_OK) throw std::runtime_error(curl_easy_strerror(code));
  long status;  // NOLINT(google-runtime-int)
  code = curl_easy_getinfo(curl.get(), CURLINFO_RESPONSE_CODE, &status);
  if (code != CURLE_OK) throw std::runtime_error(curl_easy_strerror(code));

  // Handle common errors as Status, this is not exhaustive.
  using ::google::cloud::Status;
  using ::google::cloud::StatusCode;
  if (status == 400) return Status(StatusCode::kInvalidArgument, buffer);
  if (status == 401) return Status(StatusCode::kUnauthenticated, buffer);
  if (status == 403) return Status(StatusCode::kPermissionDenied, buffer);
  if (status >= 500) return Status(StatusCode::kInternal, buffer);
  if (status < 200 || status >= 300) {
    std::ostringstream os;
    os << "HTTP error [" << status << "]: " << buffer;
    return Status(StatusCode::kUnknown, buffer);
  }
  return buffer;
}

// TODO(#6185) - this should be done by the generated code
std::set<std::string> DefaultTracingComponents() {
  return absl::StrSplit(
      google::cloud::internal::GetEnv("GOOGLE_CLOUD_CPP_ENABLE_TRACING")
          .value_or(""),
      ',');
}

google::iam::credentials::v1::GenerateAccessTokenResponse UseAccessToken(
    google::cloud::iam::IAMCredentialsClient client,
    std::vector<std::string> const& argv) {
  namespace iam = google::cloud::iam;
  return [](iam::IAMCredentialsClient client,
            std::string const& service_account, std::string const& project_id) {
    google::protobuf::Duration duration;
    duration.set_seconds(
        std::chrono::seconds(2 * kTokenValidationPeriod).count());
    auto token = client.GenerateAccessToken(
        "projects/-/serviceAccounts/" + service_account, /*delegates=*/{},
        /*scope=*/{"https://www.googleapis.com/auth/cloud-platform"}, duration);
    if (!token) throw std::runtime_error(token.status().message());

    auto const expiration =
        std::chrono::system_clock::from_time_t(token->expire_time().seconds());
    std::cout << "Fetched token starting with "
              << token->access_token().substr(0, 8)
              << ", which will expire around " << absl::FromChrono(expiration)
              << std::endl;

    namespace spanner = google::cloud::spanner;
    auto credentials = grpc::CompositeChannelCredentials(
        grpc::SslCredentials({}),
        grpc::AccessTokenCredentials(token->access_token()));

    spanner::InstanceAdminClient admin(spanner::MakeInstanceAdminConnection(
        google::cloud::Options{}.set<google::cloud::GrpcCredentialOption>(
            credentials)));
    for (auto instance : admin.ListInstances(project_id, /*filter=*/{})) {
      if (!instance) throw std::runtime_error(instance.status().message());
      std::cout << "Instance: " << instance->name() << "\n";
    }

    return *std::move(token);
  }(std::move(client), argv.at(0), argv.at(1));
}

void UseAccessTokenUntilExpired(google::cloud::iam::IAMCredentialsClient client,
                                std::vector<std::string> const& argv) {
  auto token = UseAccessToken(std::move(client), argv);
  auto const project_id = argv.at(1);
  auto const expiration =
      std::chrono::system_clock::from_time_t(token.expire_time().seconds());
  auto const deadline = expiration + 4 * kTokenValidationPeriod;
  std::cout << "Running until " << absl::FromChrono(deadline)
            << ". This is past the access token expiration time ("
            << absl::FromChrono(expiration) << ")" << std::endl;

  auto iteration = [=](bool expired) {
    namespace spanner = google::cloud::spanner;
    auto credentials = grpc::CompositeChannelCredentials(
        grpc::SslCredentials({}),
        grpc::AccessTokenCredentials(token.access_token()));
    spanner::InstanceAdminClient admin(spanner::MakeInstanceAdminConnection(
        google::cloud::Options{}.set<google::cloud::GrpcCredentialOption>(
            credentials)));
    for (auto instance : admin.ListInstances(project_id, /*filter=*/{})) {
      // kUnauthenticated receives special treatment, it is the error received
      // when the token expires.
      if (instance.status().code() ==
          google::cloud::StatusCode::kUnauthenticated) {
        std::cout << "error [" << instance.status() << "]";
        if (!expired) {
          std::cout << ": unexpected, but could be a race condition."
                    << " Trying again\n";
          return true;
        }
        std::cout << ": this is expected as the token is expired\n";
        return false;
      }
      if (!instance) throw std::runtime_error(instance.status().message());
      std::cout << "success (" << instance->name() << ")\n";
      return true;
    }
    return false;
  };

  for (auto now = std::chrono::system_clock::now(); now < deadline;
       now = std::chrono::system_clock::now()) {
    auto const expired = (now > expiration);
    std::cout << absl::FromChrono(now) << ": running iteration with "
              << (expired ? "an expired" : "a valid") << " token ";
    if (!iteration(expired)) break;
    std::this_thread::sleep_for(kTokenValidationPeriod);
  }
}

void UseIdTokenHttp(google::cloud::iam::IAMCredentialsClient client,
                    std::vector<std::string> const& argv) {
  namespace iam = google::cloud::iam;
  [](iam::IAMCredentialsClient client, std::string const& service_account,
     std::string const& hello_world_url) {
    auto token = client.GenerateIdToken(
        "projects/-/serviceAccounts/" + service_account, /*delegates=*/{},
        /*audience=*/{hello_world_url},
        /*include_email=*/true);
    if (!token) throw std::runtime_error(token.status().message());

    auto backoff = std::chrono::milliseconds(250);
    for (int i = 0; i != 3; ++i) {
      auto text = HttpGet(hello_world_url, token->token());
      if (text.ok()) {
        std::cout << "Server says: " << *text << "\n";
        return;
      }
      std::this_thread::sleep_for(backoff);
      backoff *= 2;
    }
    throw std::runtime_error("Could not contact server after 3 attempts");
  }(std::move(client), argv.at(0), argv.at(1));
}

void UseIdTokenGrpc(google::cloud::iam::IAMCredentialsClient client,
                    std::vector<std::string> const& argv) {
  namespace iam = google::cloud::iam;
  [](iam::IAMCredentialsClient client, std::string const& service_account,
     std::string const& url) {
    auto token = client.GenerateIdToken(
        "projects/-/serviceAccounts/" + service_account, /*delegates=*/{},
        /*audience=*/{url},
        /*include_email=*/true);
    if (!token) throw std::runtime_error(token.status().message());

    auto const prefix = std::string{"https://"};
    if (url.rfind(prefix, 0) != 0) {
      throw std::runtime_error("Invalid URL" + url);
    }
    auto endpoint = url.substr(prefix.length()) + ":443";
    auto credentials = grpc::CompositeChannelCredentials(
        grpc::SslCredentials(grpc::SslCredentialsOptions{}),
        grpc::AccessTokenCredentials(token->token()));
    auto channel = grpc::CreateChannel(endpoint, credentials);
    auto stub = google::cloud::examples::Greet::NewStub(channel);
    auto request = google::cloud::examples::HelloRequest{};
    auto backoff = std::chrono::milliseconds(250);
    for (int i = 0; i != 3; ++i) {
      grpc::ClientContext context;
      google::cloud::examples::HelloResponse response;
      auto status = stub->Hello(&context, request, &response);
      if (status.ok()) {
        std::cout << "Servers says: " << response.greeting() << "\n";
        return;
      }
      std::cout << "Server returned error=" << status.error_code()
                << ", message=" << status.error_message() << "\n";
      std::this_thread::sleep_for(backoff);
      backoff *= 2;
    }
    throw std::runtime_error("Could not contact server after 3 attempts");
  }(std::move(client), argv.at(0), argv.at(1));
}

void AutoRun(std::vector<std::string> const& argv) {
  namespace examples = ::google::cloud::testing_util;
  using google::cloud::internal::GetEnv;

  if (!argv.empty()) throw examples::Usage{"auto"};
  examples::CheckEnvironmentVariablesAreSet({
      "GOOGLE_CLOUD_PROJECT",
      "GOOGLE_CLOUD_CPP_IAM_TEST_SERVICE_ACCOUNT",
      "GOOGLE_CLOUD_CPP_TEST_HELLO_WORLD_SERVICE_ACCOUNT",
      "GOOGLE_CLOUD_CPP_TEST_HELLO_WORLD_HTTP_URL",
      "GOOGLE_CLOUD_CPP_TEST_HELLO_WORLD_GRPC_URL",
  });
  auto project_id = GetEnv("GOOGLE_CLOUD_PROJECT").value();
  auto const test_iam_service_account =
      GetEnv("GOOGLE_CLOUD_CPP_IAM_TEST_SERVICE_ACCOUNT").value_or("");
  auto const hello_world_service_account =
      GetEnv("GOOGLE_CLOUD_CPP_TEST_HELLO_WORLD_SERVICE_ACCOUNT").value_or("");
  auto const hello_world_http_url =
      GetEnv("GOOGLE_CLOUD_CPP_TEST_HELLO_WORLD_HTTP_URL").value_or("");
  auto const hello_world_grpc_url =
      GetEnv("GOOGLE_CLOUD_CPP_TEST_HELLO_WORLD_GRPC_URL").value_or("");

  auto client = google::cloud::iam::IAMCredentialsClient(
      google::cloud::iam::MakeIAMCredentialsConnection(
          google::cloud::Options{}
              .set<google::cloud::TracingComponentsOption>(
                  DefaultTracingComponents())
              .set<google::cloud::GrpcTracingOptionsOption>(
                  // There are some credentials returned by RPCs. On an error
                  // these are printed. This truncates them, making the output
                  // safe, and yet useful for debugging.
                  google::cloud::TracingOptions{}.SetOptions(
                      "truncate_string_field_longer_than=32"))));

  std::cout << "\nRunning UseAccessToken() example" << std::endl;
  UseAccessToken(client, {test_iam_service_account, project_id});

  std::cout << "\nRunning UseAccessTokenUntilExpired() example" << std::endl;
  UseAccessTokenUntilExpired(client, {test_iam_service_account, project_id});

  std::cout << "\nRunning UseIdTokenHttp() example" << std::endl;
  UseIdTokenHttp(client, {hello_world_service_account, hello_world_http_url});

  std::cout << "\nRunning UseIdTokenGrpc() example" << std::endl;
  UseIdTokenGrpc(client, {hello_world_service_account, hello_world_grpc_url});
}

}  // namespace

int main(int argc, char* argv[]) {  // NOLINT(bugprone-exception-escape)
  using ::google::cloud::testing_util::Example;

  using ClientCommand = std::function<void(
      google::cloud::iam::IAMCredentialsClient, std::vector<std::string> argv)>;

  auto make_entry = [](std::string name,
                       std::vector<std::string> const& arg_names,
                       ClientCommand const& command) {
    auto adapter = [=](std::vector<std::string> argv) {
      if ((argv.size() == 1 && argv[0] == "--help") ||
          argv.size() != arg_names.size()) {
        std::string usage = name;
        for (auto const& a : arg_names) usage += " <" + a + ">";
        throw google::cloud::testing_util::Usage{std::move(usage)};
      }
      auto client = google::cloud::iam::IAMCredentialsClient(
          google::cloud::iam::MakeIAMCredentialsConnection(
              google::cloud::Options{}
                  .set<google::cloud::TracingComponentsOption>(
                      DefaultTracingComponents())));
      command(client, std::move(argv));
    };
    return google::cloud::testing_util::Commands::value_type(std::move(name),
                                                             adapter);
  };

  Example example({
      make_entry("use-access-token", {"service-account", "project-id"},
                 UseAccessToken),
      make_entry("use-access-token-until-expired",
                 {"service-account", "project-id"}, UseAccessTokenUntilExpired),
      make_entry("use-id-token-http",
                 {"service-account", "hello-world-http-url"}, UseIdTokenHttp),
      make_entry("use-id-token-grpc",
                 {"service-account", "hello-world-http-url"}, UseIdTokenGrpc),
      {"auto", AutoRun},
  });
  return example.Run(argc, argv);
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2002-2005 The Regents of The University of Michigan
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Steve Reinhardt
 */

#include "arch/utility.hh"
#include "arch/faults.hh"
#include "base/cprintf.hh"
#include "base/inifile.hh"
#include "base/loader/symtab.hh"
#include "base/misc.hh"
#include "base/pollevent.hh"
#include "base/range.hh"
#include "base/stats/events.hh"
#include "base/trace.hh"
#include "cpu/base.hh"
#include "cpu/exetrace.hh"
#include "cpu/profile.hh"
#include "cpu/simple/base.hh"
#include "cpu/simple_thread.hh"
#include "cpu/smt.hh"
#include "cpu/static_inst.hh"
#include "cpu/thread_context.hh"
#include "mem/packet.hh"
#include "sim/builder.hh"
#include "sim/byteswap.hh"
#include "sim/debug.hh"
#include "sim/host.hh"
#include "sim/sim_events.hh"
#include "sim/sim_object.hh"
#include "sim/stats.hh"
#include "sim/system.hh"

#if FULL_SYSTEM
#include "arch/kernel_stats.hh"
#include "arch/stacktrace.hh"
#include "arch/tlb.hh"
#include "arch/vtophys.hh"
#include "base/remote_gdb.hh"
#else // !FULL_SYSTEM
#include "mem/mem_object.hh"
#endif // FULL_SYSTEM

using namespace std;
using namespace TheISA;

BaseSimpleCPU::BaseSimpleCPU(Params *p)
    : BaseCPU(p), traceData(NULL), thread(NULL), predecoder(NULL)
{
#if FULL_SYSTEM
    thread = new SimpleThread(this, 0, p->system, p->itb, p->dtb);
#else
    thread = new SimpleThread(this, /* thread_num */ 0, p->process,
            /* asid */ 0);
#endif // !FULL_SYSTEM

    thread->setStatus(ThreadContext::Unallocated);

    tc = thread->getTC();

    numInst = 0;
    startNumInst = 0;
    numLoad = 0;
    startNumLoad = 0;
    lastIcacheStall = 0;
    lastDcacheStall = 0;

    threadContexts.push_back(tc);

    fetchOffset = 0;
    stayAtPC = false;
}

BaseSimpleCPU::~BaseSimpleCPU()
{
}

void
BaseSimpleCPU::deallocateContext(int thread_num)
{
    // for now, these are equivalent
    suspendContext(thread_num);
}

void
BaseSimpleCPU::haltContext(int thread_num)
{
    // for now, these are equivalent
    suspendContext(thread_num);
}

void
BaseSimpleCPU::regStats()
{
    using namespace Stats;

    BaseCPU::regStats();

    numInsts
        .name(name() + ".num_insts")
        .desc("Number of instructions executed")
        ;

    numMemRefs
        .name(name() + ".num_refs")
        .desc("Number of memory references")
        ;

    notIdleFraction
        .name(name() + ".not_idle_fraction")
        .desc("Percentage of non-idle cycles")
        ;

    idleFraction
        .name(name() + ".idle_fraction")
        .desc("Percentage of idle cycles")
        ;

    icacheStallCycles
        .name(name() + ".icache_stall_cycles")
        .desc("ICache total stall cycles")
        .prereq(icacheStallCycles)
        ;

    dcacheStallCycles
        .name(name() + ".dcache_stall_cycles")
        .desc("DCache total stall cycles")
        .prereq(dcacheStallCycles)
        ;

    icacheRetryCycles
        .name(name() + ".icache_retry_cycles")
        .desc("ICache total retry cycles")
        .prereq(icacheRetryCycles)
        ;

    dcacheRetryCycles
        .name(name() + ".dcache_retry_cycles")
        .desc("DCache total retry cycles")
        .prereq(dcacheRetryCycles)
        ;

    idleFraction = constant(1.0) - notIdleFraction;
}

void
BaseSimpleCPU::resetStats()
{
//    startNumInst = numInst;
    // notIdleFraction = (_status != Idle);
}

void
BaseSimpleCPU::serialize(ostream &os)
{
    BaseCPU::serialize(os);
//    SERIALIZE_SCALAR(inst);
    nameOut(os, csprintf("%s.xc.0", name()));
    thread->serialize(os);
}

void
BaseSimpleCPU::unserialize(Checkpoint *cp, const string &section)
{
    BaseCPU::unserialize(cp, section);
//    UNSERIALIZE_SCALAR(inst);
    thread->unserialize(cp, csprintf("%s.xc.0", section));
}

void
change_thread_state(int thread_number, int activate, int priority)
{
}

Fault
BaseSimpleCPU::copySrcTranslate(Addr src)
{
#if 0
    static bool no_warn = true;
    int blk_size = (dcacheInterface) ? dcacheInterface->getBlockSize() : 64;
    // Only support block sizes of 64 atm.
    assert(blk_size == 64);
    int offset = src & (blk_size - 1);

    // Make sure block doesn't span page
    if (no_warn &&
        (src & PageMask) != ((src + blk_size) & PageMask) &&
        (src >> 40) != 0xfffffc) {
        warn("Copied block source spans pages %x.", src);
        no_warn = false;
    }

    memReq->reset(src & ~(blk_size - 1), blk_size);

    // translate to physical address
    Fault fault = thread->translateDataReadReq(req);

    if (fault == NoFault) {
        thread->copySrcAddr = src;
        thread->copySrcPhysAddr = memReq->paddr + offset;
    } else {
        assert(!fault->isAlignmentFault());

        thread->copySrcAddr = 0;
        thread->copySrcPhysAddr = 0;
    }
    return fault;
#else
    return NoFault;
#endif
}

Fault
BaseSimpleCPU::copy(Addr dest)
{
#if 0
    static bool no_warn = true;
    int blk_size = (dcacheInterface) ? dcacheInterface->getBlockSize() : 64;
    // Only support block sizes of 64 atm.
    assert(blk_size == 64);
    uint8_t data[blk_size];
    //assert(thread->copySrcAddr);
    int offset = dest & (blk_size - 1);

    // Make sure block doesn't span page
    if (no_warn &&
        (dest & PageMask) != ((dest + blk_size) & PageMask) &&
        (dest >> 40) != 0xfffffc) {
        no_warn = false;
        warn("Copied block destination spans pages %x. ", dest);
    }

    memReq->reset(dest & ~(blk_size -1), blk_size);
    // translate to physical address
    Fault fault = thread->translateDataWriteReq(req);

    if (fault == NoFault) {
        Addr dest_addr = memReq->paddr + offset;
        // Need to read straight from memory since we have more than 8 bytes.
        memReq->paddr = thread->copySrcPhysAddr;
        thread->mem->read(memReq, data);
        memReq->paddr = dest_addr;
        thread->mem->write(memReq, data);
        if (dcacheInterface) {
            memReq->cmd = Copy;
            memReq->completionEvent = NULL;
            memReq->paddr = thread->copySrcPhysAddr;
            memReq->dest = dest_addr;
            memReq->size = 64;
            memReq->time = curTick;
            memReq->flags &= ~INST_READ;
            dcacheInterface->access(memReq);
        }
    }
    else
        assert(!fault->isAlignmentFault());

    return fault;
#else
    panic("copy not implemented");
    return NoFault;
#endif
}

#if FULL_SYSTEM
Addr
BaseSimpleCPU::dbg_vtophys(Addr addr)
{
    return vtophys(tc, addr);
}
#endif // FULL_SYSTEM

#if FULL_SYSTEM
void
BaseSimpleCPU::post_interrupt(int int_num, int index)
{
    BaseCPU::post_interrupt(int_num, index);

    if (thread->status() == ThreadContext::Suspended) {
                DPRINTF(Quiesce,"Suspended Processor awoke\n");
        thread->activate();
    }
}
#endif // FULL_SYSTEM

void
BaseSimpleCPU::checkForInterrupts()
{
#if FULL_SYSTEM
    if (check_interrupts(tc)) {
        Fault interrupt = interrupts.getInterrupt(tc);

        if (interrupt != NoFault) {
            interrupts.updateIntrInfo(tc);
            interrupt->invoke(tc);
        }
    }
#endif
}

Fault
BaseSimpleCPU::setupFetchRequest(Request *req)
{
    Addr threadPC = thread->readPC();

    // set up memory request for instruction fetch
#if ISA_HAS_DELAY_SLOT
    DPRINTF(Fetch,"Fetch: PC:%08p NPC:%08p NNPC:%08p\n",threadPC,
            thread->readNextPC(),thread->readNextNPC());
#else
    DPRINTF(Fetch,"Fetch: PC:%08p NPC:%08p",threadPC,
            thread->readNextPC());
#endif

    const Addr PCMask = ~((Addr)sizeof(MachInst) - 1);
    Addr fetchPC = threadPC + fetchOffset;
    req->setVirt(0, fetchPC & PCMask, sizeof(MachInst), 0, threadPC);

    Fault fault = thread->translateInstReq(req);

    return fault;
}

void
BaseSimpleCPU::preExecute()
{
    // maintain $r0 semantics
    thread->setIntReg(ZeroReg, 0);
#if THE_ISA == ALPHA_ISA
    thread->setFloatReg(ZeroReg, 0.0);
#endif // ALPHA_ISA

    // keep an instruction count
    numInst++;
    numInsts++;

    thread->funcExeInst++;

    // check for instruction-count-based events
    comInstEventQueue[0]->serviceEvents(numInst);

    // decode the instruction
    inst = gtoh(inst);

    //If we're not in the middle of a macro instruction
    if (!curMacroStaticInst) {

        StaticInstPtr instPtr = NULL;

        //Predecode, ie bundle up an ExtMachInst
        //This should go away once the constructor can be set up properly
        predecoder.setTC(thread->getTC());
        //If more fetch data is needed, pass it in.
        const Addr PCMask = ~((Addr)sizeof(MachInst) - 1);
        if(predecoder.needMoreBytes())
            predecoder.moreBytes((thread->readPC() & PCMask) + fetchOffset,
                    0, inst);
        else
            predecoder.process();

        //If an instruction is ready, decode it. Otherwise, we'll have to
        //fetch beyond the MachInst at the current pc.
        if (predecoder.extMachInstReady()) {
#if THE_ISA == X86_ISA
            thread->setNextPC(thread->readPC() + predecoder.getInstSize());
#endif // X86_ISA
            stayAtPC = false;
            instPtr = StaticInst::decode(predecoder.getExtMachInst());
        } else {
            stayAtPC = true;
            fetchOffset += sizeof(MachInst);
        }

        //If we decoded an instruction and it's microcoded, start pulling
        //out micro ops
        if (instPtr && instPtr->isMacroOp()) {
            curMacroStaticInst = instPtr;
            curStaticInst = curMacroStaticInst->
                fetchMicroOp(thread->readMicroPC());
        } else {
            curStaticInst = instPtr;
        }
    } else {
        //Read the next micro op from the macro op
        curStaticInst = curMacroStaticInst->
            fetchMicroOp(thread->readMicroPC());
    }

#if TRACING_ON
    //If we decoded an instruction this "tick", record information about it.
    if(curStaticInst)
    {
        traceData = Trace::getInstRecord(curTick, tc, curStaticInst,
                                         thread->readPC());

        DPRINTF(Decode,"Decode: Decoded %s instruction: 0x%x\n",
                curStaticInst->getName(), curStaticInst->machInst);

#if FULL_SYSTEM
        thread->setInst(inst);
#endif // FULL_SYSTEM
    }
#endif // TRACING_ON
}

void
BaseSimpleCPU::postExecute()
{
#if FULL_SYSTEM
    if (thread->profile) {
        bool usermode = TheISA::inUserMode(tc);
        thread->profilePC = usermode ? 1 : thread->readPC();
        StaticInstPtr si(inst);
        ProfileNode *node = thread->profile->consume(tc, si);
        if (node)
            thread->profileNode = node;
    }
#endif

    if (curStaticInst->isMemRef()) {
        numMemRefs++;
    }

    if (curStaticInst->isLoad()) {
        ++numLoad;
        comLoadEventQueue[0]->serviceEvents(numLoad);
    }

    traceFunctions(thread->readPC());

    if (traceData) {
        traceData->dump();
        delete traceData;
        traceData = NULL;
    }
}

void
BaseSimpleCPU::advancePC(Fault fault)
{
    //Since we're moving to a new pc, zero out the offset
    fetchOffset = 0;
    if (fault != NoFault) {
        curMacroStaticInst = StaticInst::nullStaticInstPtr;
        fault->invoke(tc);
        thread->setMicroPC(0);
        thread->setNextMicroPC(1);
    } else {
        //If we're at the last micro op for this instruction
        if (curStaticInst && curStaticInst->isLastMicroOp()) {
            //We should be working with a macro op
            assert(curMacroStaticInst);
            //Close out this macro op, and clean up the
            //microcode state
            curMacroStaticInst = StaticInst::nullStaticInstPtr;
            thread->setMicroPC(0);
            thread->setNextMicroPC(1);
        }
        //If we're still in a macro op
        if (curMacroStaticInst) {
            //Advance the micro pc
            thread->setMicroPC(thread->readNextMicroPC());
            //Advance the "next" micro pc. Note that there are no delay
            //slots, and micro ops are "word" addressed.
            thread->setNextMicroPC(thread->readNextMicroPC() + 1);
        } else {
            // go to the next instruction
            thread->setPC(thread->readNextPC());
            thread->setNextPC(thread->readNextNPC());
            thread->setNextNPC(thread->readNextNPC() + sizeof(MachInst));
            assert(thread->readNextPC() != thread->readNextNPC());
        }
    }

#if FULL_SYSTEM
    Addr oldpc;
    do {
        oldpc = thread->readPC();
        system->pcEventQueue.service(tc);
    } while (oldpc != thread->readPC());
#endif
}

<commit_msg>Don't mask the pc because the Alpha predecoder needs it to set the PAL mode bit in the ExtMachInst.<commit_after>/*
 * Copyright (c) 2002-2005 The Regents of The University of Michigan
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Steve Reinhardt
 */

#include "arch/utility.hh"
#include "arch/faults.hh"
#include "base/cprintf.hh"
#include "base/inifile.hh"
#include "base/loader/symtab.hh"
#include "base/misc.hh"
#include "base/pollevent.hh"
#include "base/range.hh"
#include "base/stats/events.hh"
#include "base/trace.hh"
#include "cpu/base.hh"
#include "cpu/exetrace.hh"
#include "cpu/profile.hh"
#include "cpu/simple/base.hh"
#include "cpu/simple_thread.hh"
#include "cpu/smt.hh"
#include "cpu/static_inst.hh"
#include "cpu/thread_context.hh"
#include "mem/packet.hh"
#include "sim/builder.hh"
#include "sim/byteswap.hh"
#include "sim/debug.hh"
#include "sim/host.hh"
#include "sim/sim_events.hh"
#include "sim/sim_object.hh"
#include "sim/stats.hh"
#include "sim/system.hh"

#if FULL_SYSTEM
#include "arch/kernel_stats.hh"
#include "arch/stacktrace.hh"
#include "arch/tlb.hh"
#include "arch/vtophys.hh"
#include "base/remote_gdb.hh"
#else // !FULL_SYSTEM
#include "mem/mem_object.hh"
#endif // FULL_SYSTEM

using namespace std;
using namespace TheISA;

BaseSimpleCPU::BaseSimpleCPU(Params *p)
    : BaseCPU(p), traceData(NULL), thread(NULL), predecoder(NULL)
{
#if FULL_SYSTEM
    thread = new SimpleThread(this, 0, p->system, p->itb, p->dtb);
#else
    thread = new SimpleThread(this, /* thread_num */ 0, p->process,
            /* asid */ 0);
#endif // !FULL_SYSTEM

    thread->setStatus(ThreadContext::Unallocated);

    tc = thread->getTC();

    numInst = 0;
    startNumInst = 0;
    numLoad = 0;
    startNumLoad = 0;
    lastIcacheStall = 0;
    lastDcacheStall = 0;

    threadContexts.push_back(tc);

    fetchOffset = 0;
    stayAtPC = false;
}

BaseSimpleCPU::~BaseSimpleCPU()
{
}

void
BaseSimpleCPU::deallocateContext(int thread_num)
{
    // for now, these are equivalent
    suspendContext(thread_num);
}

void
BaseSimpleCPU::haltContext(int thread_num)
{
    // for now, these are equivalent
    suspendContext(thread_num);
}

void
BaseSimpleCPU::regStats()
{
    using namespace Stats;

    BaseCPU::regStats();

    numInsts
        .name(name() + ".num_insts")
        .desc("Number of instructions executed")
        ;

    numMemRefs
        .name(name() + ".num_refs")
        .desc("Number of memory references")
        ;

    notIdleFraction
        .name(name() + ".not_idle_fraction")
        .desc("Percentage of non-idle cycles")
        ;

    idleFraction
        .name(name() + ".idle_fraction")
        .desc("Percentage of idle cycles")
        ;

    icacheStallCycles
        .name(name() + ".icache_stall_cycles")
        .desc("ICache total stall cycles")
        .prereq(icacheStallCycles)
        ;

    dcacheStallCycles
        .name(name() + ".dcache_stall_cycles")
        .desc("DCache total stall cycles")
        .prereq(dcacheStallCycles)
        ;

    icacheRetryCycles
        .name(name() + ".icache_retry_cycles")
        .desc("ICache total retry cycles")
        .prereq(icacheRetryCycles)
        ;

    dcacheRetryCycles
        .name(name() + ".dcache_retry_cycles")
        .desc("DCache total retry cycles")
        .prereq(dcacheRetryCycles)
        ;

    idleFraction = constant(1.0) - notIdleFraction;
}

void
BaseSimpleCPU::resetStats()
{
//    startNumInst = numInst;
    // notIdleFraction = (_status != Idle);
}

void
BaseSimpleCPU::serialize(ostream &os)
{
    BaseCPU::serialize(os);
//    SERIALIZE_SCALAR(inst);
    nameOut(os, csprintf("%s.xc.0", name()));
    thread->serialize(os);
}

void
BaseSimpleCPU::unserialize(Checkpoint *cp, const string &section)
{
    BaseCPU::unserialize(cp, section);
//    UNSERIALIZE_SCALAR(inst);
    thread->unserialize(cp, csprintf("%s.xc.0", section));
}

void
change_thread_state(int thread_number, int activate, int priority)
{
}

Fault
BaseSimpleCPU::copySrcTranslate(Addr src)
{
#if 0
    static bool no_warn = true;
    int blk_size = (dcacheInterface) ? dcacheInterface->getBlockSize() : 64;
    // Only support block sizes of 64 atm.
    assert(blk_size == 64);
    int offset = src & (blk_size - 1);

    // Make sure block doesn't span page
    if (no_warn &&
        (src & PageMask) != ((src + blk_size) & PageMask) &&
        (src >> 40) != 0xfffffc) {
        warn("Copied block source spans pages %x.", src);
        no_warn = false;
    }

    memReq->reset(src & ~(blk_size - 1), blk_size);

    // translate to physical address
    Fault fault = thread->translateDataReadReq(req);

    if (fault == NoFault) {
        thread->copySrcAddr = src;
        thread->copySrcPhysAddr = memReq->paddr + offset;
    } else {
        assert(!fault->isAlignmentFault());

        thread->copySrcAddr = 0;
        thread->copySrcPhysAddr = 0;
    }
    return fault;
#else
    return NoFault;
#endif
}

Fault
BaseSimpleCPU::copy(Addr dest)
{
#if 0
    static bool no_warn = true;
    int blk_size = (dcacheInterface) ? dcacheInterface->getBlockSize() : 64;
    // Only support block sizes of 64 atm.
    assert(blk_size == 64);
    uint8_t data[blk_size];
    //assert(thread->copySrcAddr);
    int offset = dest & (blk_size - 1);

    // Make sure block doesn't span page
    if (no_warn &&
        (dest & PageMask) != ((dest + blk_size) & PageMask) &&
        (dest >> 40) != 0xfffffc) {
        no_warn = false;
        warn("Copied block destination spans pages %x. ", dest);
    }

    memReq->reset(dest & ~(blk_size -1), blk_size);
    // translate to physical address
    Fault fault = thread->translateDataWriteReq(req);

    if (fault == NoFault) {
        Addr dest_addr = memReq->paddr + offset;
        // Need to read straight from memory since we have more than 8 bytes.
        memReq->paddr = thread->copySrcPhysAddr;
        thread->mem->read(memReq, data);
        memReq->paddr = dest_addr;
        thread->mem->write(memReq, data);
        if (dcacheInterface) {
            memReq->cmd = Copy;
            memReq->completionEvent = NULL;
            memReq->paddr = thread->copySrcPhysAddr;
            memReq->dest = dest_addr;
            memReq->size = 64;
            memReq->time = curTick;
            memReq->flags &= ~INST_READ;
            dcacheInterface->access(memReq);
        }
    }
    else
        assert(!fault->isAlignmentFault());

    return fault;
#else
    panic("copy not implemented");
    return NoFault;
#endif
}

#if FULL_SYSTEM
Addr
BaseSimpleCPU::dbg_vtophys(Addr addr)
{
    return vtophys(tc, addr);
}
#endif // FULL_SYSTEM

#if FULL_SYSTEM
void
BaseSimpleCPU::post_interrupt(int int_num, int index)
{
    BaseCPU::post_interrupt(int_num, index);

    if (thread->status() == ThreadContext::Suspended) {
                DPRINTF(Quiesce,"Suspended Processor awoke\n");
        thread->activate();
    }
}
#endif // FULL_SYSTEM

void
BaseSimpleCPU::checkForInterrupts()
{
#if FULL_SYSTEM
    if (check_interrupts(tc)) {
        Fault interrupt = interrupts.getInterrupt(tc);

        if (interrupt != NoFault) {
            interrupts.updateIntrInfo(tc);
            interrupt->invoke(tc);
        }
    }
#endif
}

Fault
BaseSimpleCPU::setupFetchRequest(Request *req)
{
    Addr threadPC = thread->readPC();

    // set up memory request for instruction fetch
#if ISA_HAS_DELAY_SLOT
    DPRINTF(Fetch,"Fetch: PC:%08p NPC:%08p NNPC:%08p\n",threadPC,
            thread->readNextPC(),thread->readNextNPC());
#else
    DPRINTF(Fetch,"Fetch: PC:%08p NPC:%08p",threadPC,
            thread->readNextPC());
#endif

    const Addr PCMask = ~((Addr)sizeof(MachInst) - 1);
    Addr fetchPC = threadPC + fetchOffset;
    req->setVirt(0, fetchPC & PCMask, sizeof(MachInst), 0, threadPC);

    Fault fault = thread->translateInstReq(req);

    return fault;
}

void
BaseSimpleCPU::preExecute()
{
    // maintain $r0 semantics
    thread->setIntReg(ZeroReg, 0);
#if THE_ISA == ALPHA_ISA
    thread->setFloatReg(ZeroReg, 0.0);
#endif // ALPHA_ISA

    // keep an instruction count
    numInst++;
    numInsts++;

    thread->funcExeInst++;

    // check for instruction-count-based events
    comInstEventQueue[0]->serviceEvents(numInst);

    // decode the instruction
    inst = gtoh(inst);

    //If we're not in the middle of a macro instruction
    if (!curMacroStaticInst) {

        StaticInstPtr instPtr = NULL;

        //Predecode, ie bundle up an ExtMachInst
        //This should go away once the constructor can be set up properly
        predecoder.setTC(thread->getTC());
        //If more fetch data is needed, pass it in.
        if(predecoder.needMoreBytes())
            predecoder.moreBytes(thread->readPC() + fetchOffset, 0, inst);
        else
            predecoder.process();

        //If an instruction is ready, decode it. Otherwise, we'll have to
        //fetch beyond the MachInst at the current pc.
        if (predecoder.extMachInstReady()) {
#if THE_ISA == X86_ISA
            thread->setNextPC(thread->readPC() + predecoder.getInstSize());
#endif // X86_ISA
            stayAtPC = false;
            instPtr = StaticInst::decode(predecoder.getExtMachInst());
        } else {
            stayAtPC = true;
            fetchOffset += sizeof(MachInst);
        }

        //If we decoded an instruction and it's microcoded, start pulling
        //out micro ops
        if (instPtr && instPtr->isMacroOp()) {
            curMacroStaticInst = instPtr;
            curStaticInst = curMacroStaticInst->
                fetchMicroOp(thread->readMicroPC());
        } else {
            curStaticInst = instPtr;
        }
    } else {
        //Read the next micro op from the macro op
        curStaticInst = curMacroStaticInst->
            fetchMicroOp(thread->readMicroPC());
    }

#if TRACING_ON
    //If we decoded an instruction this "tick", record information about it.
    if(curStaticInst)
    {
        traceData = Trace::getInstRecord(curTick, tc, curStaticInst,
                                         thread->readPC());

        DPRINTF(Decode,"Decode: Decoded %s instruction: 0x%x\n",
                curStaticInst->getName(), curStaticInst->machInst);

#if FULL_SYSTEM
        thread->setInst(inst);
#endif // FULL_SYSTEM
    }
#endif // TRACING_ON
}

void
BaseSimpleCPU::postExecute()
{
#if FULL_SYSTEM
    if (thread->profile) {
        bool usermode = TheISA::inUserMode(tc);
        thread->profilePC = usermode ? 1 : thread->readPC();
        StaticInstPtr si(inst);
        ProfileNode *node = thread->profile->consume(tc, si);
        if (node)
            thread->profileNode = node;
    }
#endif

    if (curStaticInst->isMemRef()) {
        numMemRefs++;
    }

    if (curStaticInst->isLoad()) {
        ++numLoad;
        comLoadEventQueue[0]->serviceEvents(numLoad);
    }

    traceFunctions(thread->readPC());

    if (traceData) {
        traceData->dump();
        delete traceData;
        traceData = NULL;
    }
}

void
BaseSimpleCPU::advancePC(Fault fault)
{
    //Since we're moving to a new pc, zero out the offset
    fetchOffset = 0;
    if (fault != NoFault) {
        curMacroStaticInst = StaticInst::nullStaticInstPtr;
        fault->invoke(tc);
        thread->setMicroPC(0);
        thread->setNextMicroPC(1);
    } else {
        //If we're at the last micro op for this instruction
        if (curStaticInst && curStaticInst->isLastMicroOp()) {
            //We should be working with a macro op
            assert(curMacroStaticInst);
            //Close out this macro op, and clean up the
            //microcode state
            curMacroStaticInst = StaticInst::nullStaticInstPtr;
            thread->setMicroPC(0);
            thread->setNextMicroPC(1);
        }
        //If we're still in a macro op
        if (curMacroStaticInst) {
            //Advance the micro pc
            thread->setMicroPC(thread->readNextMicroPC());
            //Advance the "next" micro pc. Note that there are no delay
            //slots, and micro ops are "word" addressed.
            thread->setNextMicroPC(thread->readNextMicroPC() + 1);
        } else {
            // go to the next instruction
            thread->setPC(thread->readNextPC());
            thread->setNextPC(thread->readNextNPC());
            thread->setNextNPC(thread->readNextNPC() + sizeof(MachInst));
            assert(thread->readNextPC() != thread->readNextNPC());
        }
    }

#if FULL_SYSTEM
    Addr oldpc;
    do {
        oldpc = thread->readPC();
        system->pcEventQueue.service(tc);
    } while (oldpc != thread->readPC());
#endif
}

<|endoftext|>


<commit_before>/*
 * Copyright (c) 2002-2005 The Regents of The University of Michigan
 * Copyright (c) 2009 The University of Edinburgh
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Gabe Black
 *          Timothy M. Jones
 */

#ifndef __CPU_TRANSLATION_HH__
#define __CPU_TRANSLATION_HH__

#include "sim/tlb.hh"

class WholeTranslationState
{
  protected:
    int outstanding;
    Fault faults[2];

  public:
    bool isSplit;
    RequestPtr mainReq;
    RequestPtr sreqLow;
    RequestPtr sreqHigh;
    uint8_t *data;
    uint64_t *res;
    BaseTLB::Mode mode;

    /** Single translation state. */
    WholeTranslationState(RequestPtr _req, uint8_t *_data, uint64_t *_res,
                          BaseTLB::Mode _mode)
        : outstanding(1), isSplit(false), mainReq(_req), sreqLow(NULL),
          sreqHigh(NULL), data(_data), res(_res), mode(_mode)
    {
        faults[0] = faults[1] = NoFault;
        assert(mode == BaseTLB::Read || mode == BaseTLB::Write);
    }

    /** Split translation state. */
    WholeTranslationState(RequestPtr _req, RequestPtr _sreqLow,
                          RequestPtr _sreqHigh, uint8_t *_data, uint64_t *_res,
                          BaseTLB::Mode _mode)
        : outstanding(2), isSplit(true), mainReq(_req), sreqLow(_sreqLow),
          sreqHigh(_sreqHigh), data(_data), res(_res), mode(_mode)
    {
        faults[0] = faults[1] = NoFault;
        assert(mode == BaseTLB::Read || mode == BaseTLB::Write);
    }

    bool
    finish(Fault fault, int index)
    {
        assert(outstanding);
        faults[index] = fault;
        outstanding--;
        if (isSplit && outstanding == 0) {

            // For ease later, we copy some state to the main request.
            if (faults[0] == NoFault) {
                mainReq->setPaddr(sreqLow->getPaddr());
            }
            mainReq->setFlags(sreqLow->getFlags());
            mainReq->setFlags(sreqHigh->getFlags());
        }
        return outstanding == 0;
    }

    Fault
    getFault() const
    {
        if (!isSplit)
            return faults[0];
        else if (faults[0] != NoFault)
            return faults[0];
        else if (faults[1] != NoFault)
            return faults[1];
        else
            return NoFault;
    }

    void
    setNoFault()
    {
        faults[0] = faults[1] = NoFault;
    }

    bool
    isUncacheable() const
    {
        return mainReq->isUncacheable();
    }

    bool
    isPrefetch() const
    {
        return mainReq->isPrefetch();
    }

    Addr
    getPaddr() const
    {
        return mainReq->getPaddr();
    }

    unsigned
    getFlags()
    {
        return mainReq->getFlags();
    }

    void
    deleteReqs()
    {
        delete mainReq;
        if (isSplit) {
            delete sreqLow;
            delete sreqHigh;
        }
    }
};

template <class ExecContext>
class DataTranslation : public BaseTLB::Translation
{
  protected:
    ExecContext *xc;
    WholeTranslationState *state;
    int index;

  public:
    DataTranslation(ExecContext *_xc, WholeTranslationState* _state)
        : xc(_xc), state(_state), index(0)
    {
    }

    DataTranslation(ExecContext *_xc, WholeTranslationState* _state,
                    int _index)
        : xc(_xc), state(_state), index(_index)
    {
    }

    void
    finish(Fault fault, RequestPtr req, ThreadContext *tc,
           BaseTLB::Mode mode)
    {
        assert(state);
        assert(mode == state->mode);
        if (state->finish(fault, index)) {
            xc->finishTranslation(state);
        }
        delete this;
    }
};

#endif // __CPU_TRANSLATION_HH__
<commit_msg>CPU: Added comments to address translation classes.<commit_after>/*
 * Copyright (c) 2002-2005 The Regents of The University of Michigan
 * Copyright (c) 2009 The University of Edinburgh
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Gabe Black
 *          Timothy M. Jones
 */

#ifndef __CPU_TRANSLATION_HH__
#define __CPU_TRANSLATION_HH__

#include "sim/tlb.hh"

/**
 * This class captures the state of an address translation.  A translation
 * can be split in two if the ISA supports it and the memory access crosses
 * a page boundary.  In this case, this class is shared by two data
 * translations (below).  Otherwise it is used by a single data translation
 * class.  When each part of the translation is finished, the finish
 * function is called which will indicate whether the whole translation is
 * completed or not.  There are also functions for accessing parts of the
 * translation state which deal with the possible split correctly.
 */
class WholeTranslationState
{
  protected:
    int outstanding;
    Fault faults[2];

  public:
    bool isSplit;
    RequestPtr mainReq;
    RequestPtr sreqLow;
    RequestPtr sreqHigh;
    uint8_t *data;
    uint64_t *res;
    BaseTLB::Mode mode;

    /**
     * Single translation state.  We set the number of outstanding
     * translations to one and indicate that it is not split.
     */
    WholeTranslationState(RequestPtr _req, uint8_t *_data, uint64_t *_res,
                          BaseTLB::Mode _mode)
        : outstanding(1), isSplit(false), mainReq(_req), sreqLow(NULL),
          sreqHigh(NULL), data(_data), res(_res), mode(_mode)
    {
        faults[0] = faults[1] = NoFault;
        assert(mode == BaseTLB::Read || mode == BaseTLB::Write);
    }

    /**
     * Split translation state.  We copy all state into this class, set the
     * number of outstanding translations to two and then mark this as a
     * split translation.
     */
    WholeTranslationState(RequestPtr _req, RequestPtr _sreqLow,
                          RequestPtr _sreqHigh, uint8_t *_data, uint64_t *_res,
                          BaseTLB::Mode _mode)
        : outstanding(2), isSplit(true), mainReq(_req), sreqLow(_sreqLow),
          sreqHigh(_sreqHigh), data(_data), res(_res), mode(_mode)
    {
        faults[0] = faults[1] = NoFault;
        assert(mode == BaseTLB::Read || mode == BaseTLB::Write);
    }

    /**
     * Finish part of a translation.  If there is only one request then this
     * translation is completed.  If the request has been split in two then
     * the outstanding count determines whether the translation is complete.
     * In this case, flags from the split request are copied to the main
     * request to make it easier to access them later on.
     */
    bool
    finish(Fault fault, int index)
    {
        assert(outstanding);
        faults[index] = fault;
        outstanding--;
        if (isSplit && outstanding == 0) {

            // For ease later, we copy some state to the main request.
            if (faults[0] == NoFault) {
                mainReq->setPaddr(sreqLow->getPaddr());
            }
            mainReq->setFlags(sreqLow->getFlags());
            mainReq->setFlags(sreqHigh->getFlags());
        }
        return outstanding == 0;
    }

    /**
     * Determine whether this translation produced a fault.  Both parts of the
     * translation must be checked if this is a split translation.
     */
    Fault
    getFault() const
    {
        if (!isSplit)
            return faults[0];
        else if (faults[0] != NoFault)
            return faults[0];
        else if (faults[1] != NoFault)
            return faults[1];
        else
            return NoFault;
    }

    /** Remove all faults from the translation. */
    void
    setNoFault()
    {
        faults[0] = faults[1] = NoFault;
    }

    /**
     * Check if this request is uncacheable.  We only need to check the main
     * request because the flags will have been copied here on a split
     * translation.
     */
    bool
    isUncacheable() const
    {
        return mainReq->isUncacheable();
    }

    /**
     * Check if this request is a prefetch.  We only need to check the main
     * request because the flags will have been copied here on a split
     * translation.
     */
    bool
    isPrefetch() const
    {
        return mainReq->isPrefetch();
    }

    /** Get the physical address of this request. */
    Addr
    getPaddr() const
    {
        return mainReq->getPaddr();
    }

    /**
     * Get the flags associated with this request.  We only need to access
     * the main request because the flags will have been copied here on a
     * split translation.
     */
    unsigned
    getFlags()
    {
        return mainReq->getFlags();
    }

    /** Delete all requests that make up this translation. */
    void
    deleteReqs()
    {
        delete mainReq;
        if (isSplit) {
            delete sreqLow;
            delete sreqHigh;
        }
    }
};

/**
 * This class represents part of a data address translation.  All state for
 * the translation is held in WholeTranslationState (above).  Therefore this
 * class does not need to know whether the translation is split or not.  The
 * index variable determines this but is simply passed on to the state class.
 * When this part of the translation is completed, finish is called.  If the
 * translation state class indicate that the whole translation is complete
 * then the execution context is informed.
 */
template <class ExecContext>
class DataTranslation : public BaseTLB::Translation
{
  protected:
    ExecContext *xc;
    WholeTranslationState *state;
    int index;

  public:
    DataTranslation(ExecContext *_xc, WholeTranslationState* _state)
        : xc(_xc), state(_state), index(0)
    {
    }

    DataTranslation(ExecContext *_xc, WholeTranslationState* _state,
                    int _index)
        : xc(_xc), state(_state), index(_index)
    {
    }

    /**
     * Finish this part of the translation and indicate that the whole
     * translation is complete if the state says so.
     */
    void
    finish(Fault fault, RequestPtr req, ThreadContext *tc,
           BaseTLB::Mode mode)
    {
        assert(state);
        assert(mode == state->mode);
        if (state->finish(fault, index)) {
            xc->finishTranslation(state);
        }
        delete this;
    }
};

#endif // __CPU_TRANSLATION_HH__
<|endoftext|>


<commit_before>/**
 * @file error.hpp
 *
 * @brief Facilities for exception-based handling of Runtime API
 * errors, including a basic exception class wrapping
 * `std::runtime_error`.
 */
#pragma once
#ifndef CUDA_API_WRAPPERS_ERROR_HPP_
#define CUDA_API_WRAPPERS_ERROR_HPP_

#include <cuda/common/types.hpp>

#include <cuda_runtime_api.h>
#include <type_traits>
#include <string>
#include <stdexcept>

namespace cuda {

namespace status {

/**
 * Aliases for CUDA status codes
 *
 * @note unfortunately, this enum can't inherit from @ref cuda::status_t
 */
enum named_t : std::underlying_type<status_t>::type {
	success                         = cudaSuccess,
	missing_configuration           = cudaErrorMissingConfiguration,
	memory_allocation               = cudaErrorMemoryAllocation,
	initialization_error            = cudaErrorInitializationError,
	launch_failure                  = cudaErrorLaunchFailure,
	prior_launch_failure            = cudaErrorPriorLaunchFailure,
	launch_timeout                  = cudaErrorLaunchTimeout,
	launch_out_of_resources         = cudaErrorLaunchOutOfResources,
	invalid_kernel_function         = cudaErrorInvalidDeviceFunction,
	invalid_configuration           = cudaErrorInvalidConfiguration,
	invalid_device                  = cudaErrorInvalidDevice,
	invalid_value                   = cudaErrorInvalidValue,
	invalid_pitch_value             = cudaErrorInvalidPitchValue,
	invalid_symbol                  = cudaErrorInvalidSymbol,
	map_buffer_object_failed        = cudaErrorMapBufferObjectFailed,
	unmap_buffer_object_failed      = cudaErrorUnmapBufferObjectFailed,
	invalid_host_pointer            = cudaErrorInvalidHostPointer,
	invalid_device_pointer          = cudaErrorInvalidDevicePointer,
	invalid_texture                 = cudaErrorInvalidTexture,
	invalid_texture_binding         = cudaErrorInvalidTextureBinding,
	invalid_channel_descriptor      = cudaErrorInvalidChannelDescriptor,
	invalid_memcpy_direction        = cudaErrorInvalidMemcpyDirection,
	address_of_constant             = cudaErrorAddressOfConstant,
	texture_fetch_failed            = cudaErrorTextureFetchFailed,
	texture_not_bound               = cudaErrorTextureNotBound,
	synchronization_error           = cudaErrorSynchronizationError,
	invalid_filter_setting          = cudaErrorInvalidFilterSetting,
	invalid_norm_setting            = cudaErrorInvalidNormSetting,
	mixed_device_execution          = cudaErrorMixedDeviceExecution,
	cuda_runtime_unloading          = cudaErrorCudartUnloading,
	unknown                         = cudaErrorUnknown,
	not_yet_implemented             = cudaErrorNotYetImplemented,
	memory_value_too_large          = cudaErrorMemoryValueTooLarge,
	invalid_resource_handle         = cudaErrorInvalidResourceHandle,
	not_ready                       = cudaErrorNotReady,
	insufficient_driver             = cudaErrorInsufficientDriver,
	set_on_active_process           = cudaErrorSetOnActiveProcess,
	invalid_surface                 = cudaErrorInvalidSurface,
	no_device                       = cudaErrorNoDevice,
	ecc_uncorrectable               = cudaErrorECCUncorrectable,
	shared_object_symbol_not_found  = cudaErrorSharedObjectSymbolNotFound,
	shared_object_init_failed       = cudaErrorSharedObjectInitFailed,
	unsupported_limit               = cudaErrorUnsupportedLimit,
	duplicate_variable_name         = cudaErrorDuplicateVariableName,
	duplicate_texture_name          = cudaErrorDuplicateTextureName,
	duplicate_surface_name          = cudaErrorDuplicateSurfaceName,
	devices_unavailable             = cudaErrorDevicesUnavailable,
	invalid_kernel_image            = cudaErrorInvalidKernelImage,
	no_kernel_image_for_device      = cudaErrorNoKernelImageForDevice,
	incompatible_driver_context     = cudaErrorIncompatibleDriverContext,
	peer_access_already_enabled     = cudaErrorPeerAccessAlreadyEnabled,
	peer_access_not_enabled         = cudaErrorPeerAccessNotEnabled,
	device_already_in_use           = cudaErrorDeviceAlreadyInUse,
	profiler_disabled               = cudaErrorProfilerDisabled,
	profiler_not_initialized        = cudaErrorProfilerNotInitialized,
	profiler_already_started        = cudaErrorProfilerAlreadyStarted,
	profiler_already_stopped        = cudaErrorProfilerAlreadyStopped,
	assert                          = cudaErrorAssert,
	too_many_peers                  = cudaErrorTooManyPeers,
	host_memory_already_registered  = cudaErrorHostMemoryAlreadyRegistered,
	host_memory_not_registered      = cudaErrorHostMemoryNotRegistered,
	operating_system                = cudaErrorOperatingSystem,
	peer_access_unsupported         = cudaErrorPeerAccessUnsupported,
	launch_max_depth_exceeded       = cudaErrorLaunchMaxDepthExceeded,
	launch_file_scoped_tex          = cudaErrorLaunchFileScopedTex,
	launch_file_scoped_surf         = cudaErrorLaunchFileScopedSurf,
	sync_depth_exceeded             = cudaErrorSyncDepthExceeded,
	launch_pending_count_exceeded   = cudaErrorLaunchPendingCountExceeded,
	not_permitted                   = cudaErrorNotPermitted,
	not_supported                   = cudaErrorNotSupported,
	hardware_stack_error            = cudaErrorHardwareStackError,
	illegal_instruction             = cudaErrorIllegalInstruction,
	misaligned_address              = cudaErrorMisalignedAddress,
	invalid_address_space           = cudaErrorInvalidAddressSpace,
	invalid_pc                      = cudaErrorInvalidPc,
	illegal_address                 = cudaErrorIllegalAddress,
	invalid_ptx                     = cudaErrorInvalidPtx,
	invalid_graphics_context        = cudaErrorInvalidGraphicsContext,
	nvlink_uncorrectable            = cudaErrorNvlinkUncorrectable,
	startup_failure                 = cudaErrorStartupFailure,
	api_failure_base                = cudaErrorApiFailureBase
};

///@cond
constexpr inline bool operator==(const status_t& lhs, const named_t& rhs) { return lhs == (status_t) rhs;}
constexpr inline bool operator!=(const status_t& lhs, const named_t& rhs) { return lhs != (status_t) rhs;}
constexpr inline bool operator==(const named_t& lhs, const status_t& rhs) { return (status_t) lhs == rhs;}
constexpr inline bool operator!=(const named_t& lhs, const status_t& rhs) { return (status_t) lhs != rhs;}
///@endcond

} // namespace status

/**
 * @brief Determine whether the API call returning the specified status had succeeded
 */
constexpr inline bool is_success(status_t status)  { return status == (status_t) status::success; }

/**
 * @brief Determine whether the API call returning the specified status had failed
 */
constexpr inline bool is_failure(status_t status)  { return status != (status_t) status::success; }

/**
 * Obtain a brief textual explanation for a specified kind of CUDA Runtime API status
 * or error code.
 */
inline std::string describe(status_t status) { return cudaGetErrorString(status); }

namespace detail {

template <typename I, bool UpperCase = false>
std::string as_hex(I x)
{
	static_assert(std::is_unsigned<I>::value, "only signed representations are supported");
	unsigned num_hex_digits = 2*sizeof(I);
	if (x == 0) return "0x0";

	enum { bits_per_hex_digit = 4 }; // = log_2 of 16
	static const char* digit_characters =
		UpperCase ? "0123456789ABCDEF" : "0123456789abcdef" ;

	std::string result(num_hex_digits,'0');
	for (unsigned digit_index = 0; digit_index < num_hex_digits ; digit_index++)
	{
		size_t bit_offset = (num_hex_digits - 1 - digit_index) * bits_per_hex_digit;
		auto hexadecimal_digit = (x >> bit_offset) & 0xF;
		result[digit_index] = digit_characters[hexadecimal_digit];
	}
	return "0x0" + result.substr(result.find_first_not_of('0'), std::string::npos);
}

// TODO: Perhaps find a way to avoid the extra function, so that as_hex() can
// be called for pointer types as well? Would be easier with boost's uint<T>...
template <typename I, bool UpperCase = false>
inline std::string ptr_as_hex(const I* ptr)
{
	return as_hex((size_t) ptr);
}

} // namespace detail

/**
 * A (base?) class for exceptions raised by CUDA code; these errors are thrown by
 * essentially all CUDA Runtime API wrappers upon failure.
 *
 * A CUDA runtime error can be constructed with either just a CUDA error code
 * (=status code), or a code plus an additional message.
 */
class runtime_error : public std::runtime_error {
public:
	///@cond
	// TODO: Constructor chaining; and perhaps allow for more construction mechanisms?
	runtime_error(cuda::status_t error_code) :
		std::runtime_error(describe(error_code)),
		code_(error_code)
	{ }
	// I wonder if I should do this the other way around
	runtime_error(cuda::status_t error_code, const std::string& what_arg) :
		std::runtime_error(what_arg + ": " + describe(error_code)),
		code_(error_code)
	{ }
	///@endcond
	runtime_error(cuda::status::named_t error_code) :
		runtime_error(static_cast<cuda::status_t>(error_code)) { }
	runtime_error(cuda::status::named_t error_code, const std::string& what_arg) :
		runtime_error(static_cast<cuda::status_t>(error_code), what_arg) { }

	/**
	 * Obtain the CUDA status code which resulted in this error being thrown.
	 */
	status_t code() const { return code_; }

private:
	status_t code_;
};

// TODO: The following could use std::optiomal arguments - which would
// prevent the need for dual versions of the functions - but we're
// not writing C++17 here

/**
 * Do nothing... unless the status indicates an error, in which case
 * a @ref cuda::runtime_error exception is thrown
 *
 * @param status should be @ref cuda::status::success - otherwise an exception is thrown
 * @param message An extra description message to add to the exception
 */
inline void throw_if_error(cuda::status_t status, std::string message) noexcept(false)
{
	if (is_failure(status)) { throw runtime_error(status, message); }
}

/**
 * Does nothing - unless the status indicates an error, in which case
 * a @ref cuda::runtime_error exception is thrown
 *
 * @param status should be @ref cuda::status::success - otherwise an exception is thrown
 */
inline void throw_if_error(cuda::status_t status) noexcept(false)
{
	if (is_failure(status)) { throw runtime_error(status); }
}

enum : bool {
	dont_clear_errors = false,
	do_clear_errors    = true
};

namespace outstanding_error {

/**
 * Reset the CUDA status to @ref cuda::status::success.
 */
inline status_t clear() noexcept { return cudaGetLastError();    }

/**
 * Get the code of the last error in a CUDA-related action.
 */
inline status_t get()   noexcept { return cudaPeekAtLastError(); }

/**
 * @brief Does nothing (unless throwing an exception)
 *
 * @note similar to @ref cuda::throw_if_error, but uses the CUDA Runtime API's internal
 * state
 *
 * @throws cuda::runtime_error if the CUDA runtime API has
 * encountered previously encountered an (uncleared) error
 *
 * @param message Additional message to incldue in the exception thrown
 * @param clear_any_error When true, clears the CUDA Runtime API's state from
 * recalling errors arising from before this moment
 *
 *
 */
inline void ensure_none(
	std::string  message,
	bool         clear_any_error = do_clear_errors) noexcept(false)
{
	auto last_status = clear_any_error ? clear() : get();
	throw_if_error(last_status, message);
}

/**
 * @brief A variant of @ref ensure_none() which takes
 * a C-style string.
 *
 * @note exists so as to avoid incorrect overload resolution of
 * `ensure_none(my_c_string)` calls.
 */
inline void ensure_none(
	const char*  message,
	bool         clear_any_error = do_clear_errors) noexcept(false)
{
	return ensure_none(std::string(message), clear_any_error);
}

/**
 * @brief Does nothing (unless throwing an exception)
 *
 * @note similar to @ref throw_if_error, but uses the CUDA Runtime API's internal
 * state
 *
 * @throws cuda::runtime_error if the CUDA runtime API has
 * encountered previously encountered an (uncleared) error
 *
 * @param clear_any_error When true, clears the CUDA Runtime API's state from
 * recalling errors arising from before this oment
 */
inline void ensure_none(bool clear_any_error = do_clear_errors) noexcept(false)
{
	auto last_status = clear_any_error ? clear() : get();
	throw_if_error(last_status);
}

} // namespace outstanding_error

} // namespace cuda

#endif // CUDA_API_WRAPPERS_ERROR_HPP_
<commit_msg>Now taking a `const std::string&` rather than a by-value string in `throw_if_error()`.<commit_after>/**
 * @file error.hpp
 *
 * @brief Facilities for exception-based handling of Runtime API
 * errors, including a basic exception class wrapping
 * `std::runtime_error`.
 */
#pragma once
#ifndef CUDA_API_WRAPPERS_ERROR_HPP_
#define CUDA_API_WRAPPERS_ERROR_HPP_

#include <cuda/common/types.hpp>

#include <cuda_runtime_api.h>
#include <type_traits>
#include <string>
#include <stdexcept>

namespace cuda {

namespace status {

/**
 * Aliases for CUDA status codes
 *
 * @note unfortunately, this enum can't inherit from @ref cuda::status_t
 */
enum named_t : std::underlying_type<status_t>::type {
	success                         = cudaSuccess,
	missing_configuration           = cudaErrorMissingConfiguration,
	memory_allocation               = cudaErrorMemoryAllocation,
	initialization_error            = cudaErrorInitializationError,
	launch_failure                  = cudaErrorLaunchFailure,
	prior_launch_failure            = cudaErrorPriorLaunchFailure,
	launch_timeout                  = cudaErrorLaunchTimeout,
	launch_out_of_resources         = cudaErrorLaunchOutOfResources,
	invalid_kernel_function         = cudaErrorInvalidDeviceFunction,
	invalid_configuration           = cudaErrorInvalidConfiguration,
	invalid_device                  = cudaErrorInvalidDevice,
	invalid_value                   = cudaErrorInvalidValue,
	invalid_pitch_value             = cudaErrorInvalidPitchValue,
	invalid_symbol                  = cudaErrorInvalidSymbol,
	map_buffer_object_failed        = cudaErrorMapBufferObjectFailed,
	unmap_buffer_object_failed      = cudaErrorUnmapBufferObjectFailed,
	invalid_host_pointer            = cudaErrorInvalidHostPointer,
	invalid_device_pointer          = cudaErrorInvalidDevicePointer,
	invalid_texture                 = cudaErrorInvalidTexture,
	invalid_texture_binding         = cudaErrorInvalidTextureBinding,
	invalid_channel_descriptor      = cudaErrorInvalidChannelDescriptor,
	invalid_memcpy_direction        = cudaErrorInvalidMemcpyDirection,
	address_of_constant             = cudaErrorAddressOfConstant,
	texture_fetch_failed            = cudaErrorTextureFetchFailed,
	texture_not_bound               = cudaErrorTextureNotBound,
	synchronization_error           = cudaErrorSynchronizationError,
	invalid_filter_setting          = cudaErrorInvalidFilterSetting,
	invalid_norm_setting            = cudaErrorInvalidNormSetting,
	mixed_device_execution          = cudaErrorMixedDeviceExecution,
	cuda_runtime_unloading          = cudaErrorCudartUnloading,
	unknown                         = cudaErrorUnknown,
	not_yet_implemented             = cudaErrorNotYetImplemented,
	memory_value_too_large          = cudaErrorMemoryValueTooLarge,
	invalid_resource_handle         = cudaErrorInvalidResourceHandle,
	not_ready                       = cudaErrorNotReady,
	insufficient_driver             = cudaErrorInsufficientDriver,
	set_on_active_process           = cudaErrorSetOnActiveProcess,
	invalid_surface                 = cudaErrorInvalidSurface,
	no_device                       = cudaErrorNoDevice,
	ecc_uncorrectable               = cudaErrorECCUncorrectable,
	shared_object_symbol_not_found  = cudaErrorSharedObjectSymbolNotFound,
	shared_object_init_failed       = cudaErrorSharedObjectInitFailed,
	unsupported_limit               = cudaErrorUnsupportedLimit,
	duplicate_variable_name         = cudaErrorDuplicateVariableName,
	duplicate_texture_name          = cudaErrorDuplicateTextureName,
	duplicate_surface_name          = cudaErrorDuplicateSurfaceName,
	devices_unavailable             = cudaErrorDevicesUnavailable,
	invalid_kernel_image            = cudaErrorInvalidKernelImage,
	no_kernel_image_for_device      = cudaErrorNoKernelImageForDevice,
	incompatible_driver_context     = cudaErrorIncompatibleDriverContext,
	peer_access_already_enabled     = cudaErrorPeerAccessAlreadyEnabled,
	peer_access_not_enabled         = cudaErrorPeerAccessNotEnabled,
	device_already_in_use           = cudaErrorDeviceAlreadyInUse,
	profiler_disabled               = cudaErrorProfilerDisabled,
	profiler_not_initialized        = cudaErrorProfilerNotInitialized,
	profiler_already_started        = cudaErrorProfilerAlreadyStarted,
	profiler_already_stopped        = cudaErrorProfilerAlreadyStopped,
	assert                          = cudaErrorAssert,
	too_many_peers                  = cudaErrorTooManyPeers,
	host_memory_already_registered  = cudaErrorHostMemoryAlreadyRegistered,
	host_memory_not_registered      = cudaErrorHostMemoryNotRegistered,
	operating_system                = cudaErrorOperatingSystem,
	peer_access_unsupported         = cudaErrorPeerAccessUnsupported,
	launch_max_depth_exceeded       = cudaErrorLaunchMaxDepthExceeded,
	launch_file_scoped_tex          = cudaErrorLaunchFileScopedTex,
	launch_file_scoped_surf         = cudaErrorLaunchFileScopedSurf,
	sync_depth_exceeded             = cudaErrorSyncDepthExceeded,
	launch_pending_count_exceeded   = cudaErrorLaunchPendingCountExceeded,
	not_permitted                   = cudaErrorNotPermitted,
	not_supported                   = cudaErrorNotSupported,
	hardware_stack_error            = cudaErrorHardwareStackError,
	illegal_instruction             = cudaErrorIllegalInstruction,
	misaligned_address              = cudaErrorMisalignedAddress,
	invalid_address_space           = cudaErrorInvalidAddressSpace,
	invalid_pc                      = cudaErrorInvalidPc,
	illegal_address                 = cudaErrorIllegalAddress,
	invalid_ptx                     = cudaErrorInvalidPtx,
	invalid_graphics_context        = cudaErrorInvalidGraphicsContext,
	nvlink_uncorrectable            = cudaErrorNvlinkUncorrectable,
	startup_failure                 = cudaErrorStartupFailure,
	api_failure_base                = cudaErrorApiFailureBase
};

///@cond
constexpr inline bool operator==(const status_t& lhs, const named_t& rhs) { return lhs == (status_t) rhs;}
constexpr inline bool operator!=(const status_t& lhs, const named_t& rhs) { return lhs != (status_t) rhs;}
constexpr inline bool operator==(const named_t& lhs, const status_t& rhs) { return (status_t) lhs == rhs;}
constexpr inline bool operator!=(const named_t& lhs, const status_t& rhs) { return (status_t) lhs != rhs;}
///@endcond

} // namespace status

/**
 * @brief Determine whether the API call returning the specified status had succeeded
 */
constexpr inline bool is_success(status_t status)  { return status == (status_t) status::success; }

/**
 * @brief Determine whether the API call returning the specified status had failed
 */
constexpr inline bool is_failure(status_t status)  { return status != (status_t) status::success; }

/**
 * Obtain a brief textual explanation for a specified kind of CUDA Runtime API status
 * or error code.
 */
inline std::string describe(status_t status) { return cudaGetErrorString(status); }

namespace detail {

template <typename I, bool UpperCase = false>
std::string as_hex(I x)
{
	static_assert(std::is_unsigned<I>::value, "only signed representations are supported");
	unsigned num_hex_digits = 2*sizeof(I);
	if (x == 0) return "0x0";

	enum { bits_per_hex_digit = 4 }; // = log_2 of 16
	static const char* digit_characters =
		UpperCase ? "0123456789ABCDEF" : "0123456789abcdef" ;

	std::string result(num_hex_digits,'0');
	for (unsigned digit_index = 0; digit_index < num_hex_digits ; digit_index++)
	{
		size_t bit_offset = (num_hex_digits - 1 - digit_index) * bits_per_hex_digit;
		auto hexadecimal_digit = (x >> bit_offset) & 0xF;
		result[digit_index] = digit_characters[hexadecimal_digit];
	}
	return "0x0" + result.substr(result.find_first_not_of('0'), std::string::npos);
}

// TODO: Perhaps find a way to avoid the extra function, so that as_hex() can
// be called for pointer types as well? Would be easier with boost's uint<T>...
template <typename I, bool UpperCase = false>
inline std::string ptr_as_hex(const I* ptr)
{
	return as_hex((size_t) ptr);
}

} // namespace detail

/**
 * A (base?) class for exceptions raised by CUDA code; these errors are thrown by
 * essentially all CUDA Runtime API wrappers upon failure.
 *
 * A CUDA runtime error can be constructed with either just a CUDA error code
 * (=status code), or a code plus an additional message.
 */
class runtime_error : public std::runtime_error {
public:
	///@cond
	// TODO: Constructor chaining; and perhaps allow for more construction mechanisms?
	runtime_error(cuda::status_t error_code) :
		std::runtime_error(describe(error_code)),
		code_(error_code)
	{ }
	// I wonder if I should do this the other way around
	runtime_error(cuda::status_t error_code, const std::string& what_arg) :
		std::runtime_error(what_arg + ": " + describe(error_code)),
		code_(error_code)
	{ }
	///@endcond
	runtime_error(cuda::status::named_t error_code) :
		runtime_error(static_cast<cuda::status_t>(error_code)) { }
	runtime_error(cuda::status::named_t error_code, const std::string& what_arg) :
		runtime_error(static_cast<cuda::status_t>(error_code), what_arg) { }

	/**
	 * Obtain the CUDA status code which resulted in this error being thrown.
	 */
	status_t code() const { return code_; }

private:
	status_t code_;
};

// TODO: The following could use std::optiomal arguments - which would
// prevent the need for dual versions of the functions - but we're
// not writing C++17 here

/**
 * Do nothing... unless the status indicates an error, in which case
 * a @ref cuda::runtime_error exception is thrown
 *
 * @param status should be @ref cuda::status::success - otherwise an exception is thrown
 * @param message An extra description message to add to the exception
 */
inline void throw_if_error(cuda::status_t status, const std::string& message) noexcept(false)
{
	if (is_failure(status)) { throw runtime_error(status, message); }
}

/**
 * Does nothing - unless the status indicates an error, in which case
 * a @ref cuda::runtime_error exception is thrown
 *
 * @param status should be @ref cuda::status::success - otherwise an exception is thrown
 */
inline void throw_if_error(cuda::status_t status) noexcept(false)
{
	if (is_failure(status)) { throw runtime_error(status); }
}

enum : bool {
	dont_clear_errors = false,
	do_clear_errors    = true
};

namespace outstanding_error {

/**
 * Reset the CUDA status to @ref cuda::status::success.
 */
inline status_t clear() noexcept { return cudaGetLastError();    }

/**
 * Get the code of the last error in a CUDA-related action.
 */
inline status_t get()   noexcept { return cudaPeekAtLastError(); }

/**
 * @brief Does nothing (unless throwing an exception)
 *
 * @note similar to @ref cuda::throw_if_error, but uses the CUDA Runtime API's internal
 * state
 *
 * @throws cuda::runtime_error if the CUDA runtime API has
 * encountered previously encountered an (uncleared) error
 *
 * @param message Additional message to incldue in the exception thrown
 * @param clear_any_error When true, clears the CUDA Runtime API's state from
 * recalling errors arising from before this moment
 *
 *
 */
inline void ensure_none(
	std::string  message,
	bool         clear_any_error = do_clear_errors) noexcept(false)
{
	auto last_status = clear_any_error ? clear() : get();
	throw_if_error(last_status, message);
}

/**
 * @brief A variant of @ref ensure_none() which takes
 * a C-style string.
 *
 * @note exists so as to avoid incorrect overload resolution of
 * `ensure_none(my_c_string)` calls.
 */
inline void ensure_none(
	const char*  message,
	bool         clear_any_error = do_clear_errors) noexcept(false)
{
	return ensure_none(std::string(message), clear_any_error);
}

/**
 * @brief Does nothing (unless throwing an exception)
 *
 * @note similar to @ref throw_if_error, but uses the CUDA Runtime API's internal
 * state
 *
 * @throws cuda::runtime_error if the CUDA runtime API has
 * encountered previously encountered an (uncleared) error
 *
 * @param clear_any_error When true, clears the CUDA Runtime API's state from
 * recalling errors arising from before this oment
 */
inline void ensure_none(bool clear_any_error = do_clear_errors) noexcept(false)
{
	auto last_status = clear_any_error ? clear() : get();
	throw_if_error(last_status);
}

} // namespace outstanding_error

} // namespace cuda

#endif // CUDA_API_WRAPPERS_ERROR_HPP_
<|endoftext|>


<commit_before>#include <iostream>

using namespace std;

int main()
{
  cout << "ohai\n";
  cin.get();

  return 0;
}
<commit_msg>unpeeving<commit_after>#include <iostream>

int main()
{
  std::cout << "ohai\n";
  return 0;
}
<|endoftext|>