<commit_before>/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * 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 <fcntl.h>
#include <getopt.h>
#include <sys/stat.h>
#include <unistd.h>

#include <fstream>
#include <iostream>
#include <iterator>
#include <sstream>
#include <string>

#include "perfetto/base/logging.h"
#include "perfetto/base/scoped_file.h"
#include "perfetto/base/unix_task_runner.h"
#include "perfetto/base/utils.h"
#include "perfetto/protozero/proto_utils.h"
#include "perfetto/traced/traced.h"
#include "perfetto/tracing/core/consumer.h"
#include "perfetto/tracing/core/data_source_config.h"
#include "perfetto/tracing/core/data_source_descriptor.h"
#include "perfetto/tracing/core/trace_config.h"
#include "perfetto/tracing/core/trace_packet.h"
#include "perfetto/tracing/ipc/consumer_ipc_client.h"

#include "perfetto/config/trace_config.pb.h"

#if defined(PERFETTO_BUILD_WITH_ANDROID)
#include "perfetto/base/android_task_runner.h"

#include <android/os/DropBoxManager.h>
#include <utils/Looper.h>
#include <utils/StrongPointer.h>
#endif  // defined(PERFETTO_BUILD_WITH_ANDROID)

// TODO(primiano): add the ability to pass the file descriptor directly to the
// traced service instead of receiving a copy of the chunks and writing them
// from this process.
namespace perfetto {
namespace {
const char kTempTraceDir[] = "/data/misc/perfetto-traces";
const char kDefaultDropBoxTag[] = "perfetto";
}  // namespace

using protozero::proto_utils::WriteVarInt;
using protozero::proto_utils::MakeTagLengthDelimited;

#if defined(PERFETTO_BUILD_WITH_ANDROID)
using PlatformTaskRunner = base::AndroidTaskRunner;
#else
using PlatformTaskRunner = base::UnixTaskRunner;
#endif

class PerfettoCmd : public Consumer {
 public:
  int Main(int argc, char** argv);
  int PrintUsage(const char* argv0);
  void OnStopTraceTimer();
  void OnTimeout();

  // perfetto::Consumer implementation.
  void OnConnect() override;
  void OnDisconnect() override;
  void OnTraceData(std::vector<TracePacket>, bool has_more) override;

 private:
  base::ScopedFile CreateTemporaryFile(std::string* out_path);
  void SaveTraceFileAs(const std::string& name);

  PlatformTaskRunner task_runner_;
  std::unique_ptr<perfetto::Service::ConsumerEndpoint> consumer_endpoint_;
  std::unique_ptr<TraceConfig> trace_config_;
  base::ScopedFstream trace_out_stream_;
  std::string trace_out_path_;

  // Only used if linkat(AT_FDCWD) isn't available.
  std::string tmp_trace_out_path_;

  std::string dropbox_tag_;
  bool did_process_full_trace_ = false;
};

int PerfettoCmd::PrintUsage(const char* argv0) {
  PERFETTO_ELOG(R"(
Usage: %s
  --background  -b     : Exits immediately and continues tracing in background
  --config      -c     : /path/to/trace/config/file or - for stdin
  --out         -o     : /path/to/out/trace/file
  --dropbox     -d TAG : Upload trace into DropBox using tag TAG (default: %s)
  --help        -h
)",
                argv0, kDefaultDropBoxTag);
  return 1;
}

int PerfettoCmd::Main(int argc, char** argv) {
  static const struct option long_options[] = {
      // |option_index| relies on the order of options, don't reshuffle them.
      {"help", required_argument, 0, 'h'},
      {"config", required_argument, 0, 'c'},
      {"out", required_argument, 0, 'o'},
      {"background", no_argument, 0, 'b'},
      {"dropbox", optional_argument, 0, 'd'},
      {nullptr, 0, nullptr, 0}};

  int option_index = 0;
  std::string trace_config_raw;
  bool background = false;
  for (;;) {
    int option =
        getopt_long(argc, argv, "c:o:bd::", long_options, &option_index);

    if (option == -1)
      break;  // EOF.

    if (option == 'c') {
      if (strcmp(optarg, "-") == 0) {
        std::istreambuf_iterator<char> begin(std::cin), end;
        trace_config_raw.assign(begin, end);
      } else if (strcmp(optarg, ":test") == 0) {
        // TODO(primiano): temporary for testing only.
        perfetto::protos::TraceConfig test_config;
        test_config.add_buffers()->set_size_kb(4096);
        test_config.set_duration_ms(10000);
        auto* ds_config = test_config.add_data_sources()->mutable_config();
        ds_config->set_name("com.google.perfetto.ftrace");
        ds_config->mutable_ftrace_config()->add_event_names("sched_switch");
        ds_config->mutable_ftrace_config()->add_event_names("cpu_idle");
        ds_config->mutable_ftrace_config()->add_event_names("cpu_frequency");
        ds_config->set_target_buffer(0);
        test_config.SerializeToString(&trace_config_raw);
      } else {
        std::ifstream file_stream;
        file_stream.open(optarg, std::ios_base::in | std::ios_base::binary);
        if (!file_stream.is_open()) {
          PERFETTO_ELOG("Could not open %s", optarg);
          return 1;
        }
        std::istreambuf_iterator<char> begin(file_stream), end;
        trace_config_raw.assign(begin, end);
      }
      continue;
    }

    if (option == 'o') {
      trace_out_path_ = optarg;
      continue;
    }

    if (option == 'd') {
#if defined(PERFETTO_BUILD_WITH_ANDROID)
      dropbox_tag_ = optarg ? optarg : kDefaultDropBoxTag;
      continue;
#else
      PERFETTO_ELOG("DropBox is only supported with Android tree builds");
      return 1;
#endif
    }

    if (option == 'b') {
      background = true;
      continue;
    }
    return PrintUsage(argv[0]);
  }

  if (!trace_out_path_.empty() && !dropbox_tag_.empty()) {
    PERFETTO_ELOG(
        "Can't log to a file (--out) and DropBox (--dropbox) at the same "
        "time");
    return 1;
  }

  if (trace_config_raw.empty() ||
      (trace_out_path_.empty() && dropbox_tag_.empty())) {
    return PrintUsage(argv[0]);
  }

  if (access(kTempTraceDir, F_OK) == -1 && mkdir(kTempTraceDir, 0770) == -1) {
    PERFETTO_ELOG("Could not create temporary trace directory: %s",
                  kTempTraceDir);
    return 1;
  }

  base::ScopedFile fd;
  fd = CreateTemporaryFile(&tmp_trace_out_path_);
  trace_out_stream_.reset(fdopen(fd.release(), "wb"));
  PERFETTO_CHECK(trace_out_stream_);

  perfetto::protos::TraceConfig trace_config_proto;
  PERFETTO_DLOG("Parsing TraceConfig, %zu bytes", trace_config_raw.size());
  bool parsed = trace_config_proto.ParseFromString(trace_config_raw);
  if (!parsed) {
    PERFETTO_ELOG("Could not parse TraceConfig proto from stdin");
    return 1;
  }
  trace_config_.reset(new TraceConfig());
  trace_config_->FromProto(trace_config_proto);
  trace_config_raw.clear();

  if (background) {
    PERFETTO_CHECK(daemon(0 /*nochdir*/, 0 /*noclose*/) == 0);
    PERFETTO_DLOG("Continuing in background");
  }

  consumer_endpoint_ = ConsumerIPCClient::Connect(PERFETTO_CONSUMER_SOCK_NAME,
                                                  this, &task_runner_);
  task_runner_.Run();
  return did_process_full_trace_ ? 0 : 1;
}  // namespace perfetto

void PerfettoCmd::OnConnect() {
  PERFETTO_LOG(
      "Connected to the Perfetto traced service, starting tracing for %d ms",
      trace_config_->duration_ms());
  PERFETTO_DCHECK(trace_config_);
  consumer_endpoint_->EnableTracing(*trace_config_);
  task_runner_.PostDelayedTask(std::bind(&PerfettoCmd::OnStopTraceTimer, this),
                               trace_config_->duration_ms());

  // Failsafe mechanism to avoid waiting indefinitely if the service hangs.
  task_runner_.PostDelayedTask(std::bind(&PerfettoCmd::OnTimeout, this),
                               trace_config_->duration_ms() * 2);
}

void PerfettoCmd::OnDisconnect() {
  PERFETTO_LOG("Disconnected from the Perfetto traced service");
  task_runner_.Quit();
}

void PerfettoCmd::OnStopTraceTimer() {
  PERFETTO_LOG("Timer expired, disabling tracing and collecting results");
  consumer_endpoint_->DisableTracing();
  consumer_endpoint_->ReadBuffers();
}

void PerfettoCmd::OnTimeout() {
  PERFETTO_ELOG("Timed out while waiting for trace from the service, aborting");
  task_runner_.Quit();
}

void PerfettoCmd::OnTraceData(std::vector<TracePacket> packets, bool has_more) {
  PERFETTO_DLOG("Received trace packet, has_more=%d", has_more);
  for (TracePacket& packet : packets) {
    for (const Chunk& chunk : packet) {
      uint8_t preamble[16];
      uint8_t* pos = preamble;
      pos = WriteVarInt(MakeTagLengthDelimited(1 /* field_id */), pos);
      pos = WriteVarInt(static_cast<uint32_t>(chunk.size), pos);
      fwrite(reinterpret_cast<const char*>(preamble), pos - preamble, 1,
             trace_out_stream_.get());
      fwrite(reinterpret_cast<const char*>(chunk.start), chunk.size, 1,
             trace_out_stream_.get());
    }
  }
  if (has_more)
    return;

  // Reached end of trace.
  consumer_endpoint_->FreeBuffers();
  task_runner_.Quit();

  long bytes_written = ftell(trace_out_stream_.get());
  if (!dropbox_tag_.empty()) {
#if defined(PERFETTO_BUILD_WITH_ANDROID)
    // DropBox needs a path to the uploaded file, so make a temporarily visible
    // file.
    // TODO(skyostil): Modify DropBox to take an fd directly.
    std::string tmp_path;
    CreateTemporaryFile(&tmp_path);
    SaveTraceFileAs(tmp_path);

    android::sp<android::os::DropBoxManager> dropbox =
        new android::os::DropBoxManager();
    android::binder::Status status = dropbox->addFile(
        android::String16(dropbox_tag_.c_str()), tmp_path, 0 /* flags */);
    unlink(tmp_path.c_str());
    if (!status.isOk()) {
      PERFETTO_ELOG("DropBox upload failed: %s", status.toString8().c_str());
      return;
    }
    PERFETTO_ILOG("Uploaded %ld bytes into DropBox with tag %s", bytes_written,
                  dropbox_tag_.c_str());
#endif  // defined(PERFETTO_BUILD_WITH_ANDROID)
  } else {
    SaveTraceFileAs(trace_out_path_);
    PERFETTO_ILOG("Wrote %ld bytes into %s", bytes_written,
                  trace_out_path_.c_str());
  }
  did_process_full_trace_ = true;
}

base::ScopedFile PerfettoCmd::CreateTemporaryFile(std::string* out_path) {
  *out_path = std::string(kTempTraceDir) + "/perfetto-traceXXXXXX";
  return base::ScopedFile(mkstemp(&(*out_path)[0]));
}

void PerfettoCmd::SaveTraceFileAs(const std::string& name) {
  PERFETTO_DCHECK(trace_out_stream_);
  PERFETTO_CHECK(rename(tmp_trace_out_path_.c_str(), name.c_str()) == 0);
  trace_out_stream_.reset();
}

int __attribute__((visibility("default")))
PerfettoCmdMain(int argc, char** argv) {
  perfetto::PerfettoCmd consumer_cmd;
  return consumer_cmd.Main(argc, argv);
}

}  // namespace perfetto
<commit_msg>Pass trace fd directly to DropBox am: 0b99a3bbd5 am: 0ff07c228f<commit_after>/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * 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 <fcntl.h>
#include <getopt.h>
#include <sys/stat.h>
#include <unistd.h>

#include <fstream>
#include <iostream>
#include <iterator>
#include <sstream>
#include <string>

#include "perfetto/base/logging.h"
#include "perfetto/base/scoped_file.h"
#include "perfetto/base/unix_task_runner.h"
#include "perfetto/base/utils.h"
#include "perfetto/protozero/proto_utils.h"
#include "perfetto/traced/traced.h"
#include "perfetto/tracing/core/consumer.h"
#include "perfetto/tracing/core/data_source_config.h"
#include "perfetto/tracing/core/data_source_descriptor.h"
#include "perfetto/tracing/core/trace_config.h"
#include "perfetto/tracing/core/trace_packet.h"
#include "perfetto/tracing/ipc/consumer_ipc_client.h"

#include "perfetto/config/trace_config.pb.h"

#if defined(PERFETTO_BUILD_WITH_ANDROID)
#include "perfetto/base/android_task_runner.h"

#include <android/os/DropBoxManager.h>
#include <utils/Looper.h>
#include <utils/StrongPointer.h>
#endif  // defined(PERFETTO_BUILD_WITH_ANDROID)

// TODO(primiano): add the ability to pass the file descriptor directly to the
// traced service instead of receiving a copy of the chunks and writing them
// from this process.
namespace perfetto {
namespace {

// Temporary directory for DropBox traces. Note that this is automatically
// created by the system by setting setprop persist.traced.enable=1.
const char kTempDropBoxTraceDir[] = "/data/misc/perfetto-traces";
const char kDefaultDropBoxTag[] = "perfetto";

std::string GetDirName(const std::string& path) {
  size_t sep = path.find_last_of('/');
  if (sep == std::string::npos)
    return ".";
  return path.substr(0, sep);
}

}  // namespace

using protozero::proto_utils::WriteVarInt;
using protozero::proto_utils::MakeTagLengthDelimited;

#if defined(PERFETTO_BUILD_WITH_ANDROID)
using PlatformTaskRunner = base::AndroidTaskRunner;
#else
using PlatformTaskRunner = base::UnixTaskRunner;
#endif

class PerfettoCmd : public Consumer {
 public:
  int Main(int argc, char** argv);
  int PrintUsage(const char* argv0);
  void OnStopTraceTimer();
  void OnTimeout();

  // perfetto::Consumer implementation.
  void OnConnect() override;
  void OnDisconnect() override;
  void OnTraceData(std::vector<TracePacket>, bool has_more) override;

 private:
  bool OpenOutputFile();

  PlatformTaskRunner task_runner_;
  std::unique_ptr<perfetto::Service::ConsumerEndpoint> consumer_endpoint_;
  std::unique_ptr<TraceConfig> trace_config_;
  base::ScopedFstream trace_out_stream_;
  std::string trace_out_path_;

  // Only used if linkat(AT_FDCWD) isn't available.
  std::string tmp_trace_out_path_;

  std::string dropbox_tag_;
  bool did_process_full_trace_ = false;
};

int PerfettoCmd::PrintUsage(const char* argv0) {
  PERFETTO_ELOG(R"(
Usage: %s
  --background  -b     : Exits immediately and continues tracing in background
  --config      -c     : /path/to/trace/config/file or - for stdin
  --out         -o     : /path/to/out/trace/file
  --dropbox     -d TAG : Upload trace into DropBox using tag TAG (default: %s)
  --help        -h
)",
                argv0, kDefaultDropBoxTag);
  return 1;
}

int PerfettoCmd::Main(int argc, char** argv) {
  static const struct option long_options[] = {
      // |option_index| relies on the order of options, don't reshuffle them.
      {"help", required_argument, 0, 'h'},
      {"config", required_argument, 0, 'c'},
      {"out", required_argument, 0, 'o'},
      {"background", no_argument, 0, 'b'},
      {"dropbox", optional_argument, 0, 'd'},
      {nullptr, 0, nullptr, 0}};

  int option_index = 0;
  std::string trace_config_raw;
  bool background = false;
  for (;;) {
    int option =
        getopt_long(argc, argv, "c:o:bd::", long_options, &option_index);

    if (option == -1)
      break;  // EOF.

    if (option == 'c') {
      if (strcmp(optarg, "-") == 0) {
        std::istreambuf_iterator<char> begin(std::cin), end;
        trace_config_raw.assign(begin, end);
      } else if (strcmp(optarg, ":test") == 0) {
        // TODO(primiano): temporary for testing only.
        perfetto::protos::TraceConfig test_config;
        test_config.add_buffers()->set_size_kb(4096);
        test_config.set_duration_ms(10000);
        auto* ds_config = test_config.add_data_sources()->mutable_config();
        ds_config->set_name("com.google.perfetto.ftrace");
        ds_config->mutable_ftrace_config()->add_event_names("sched_switch");
        ds_config->mutable_ftrace_config()->add_event_names("cpu_idle");
        ds_config->mutable_ftrace_config()->add_event_names("cpu_frequency");
        ds_config->set_target_buffer(0);
        test_config.SerializeToString(&trace_config_raw);
      } else {
        std::ifstream file_stream;
        file_stream.open(optarg, std::ios_base::in | std::ios_base::binary);
        if (!file_stream.is_open()) {
          PERFETTO_ELOG("Could not open %s", optarg);
          return 1;
        }
        std::istreambuf_iterator<char> begin(file_stream), end;
        trace_config_raw.assign(begin, end);
      }
      continue;
    }

    if (option == 'o') {
      trace_out_path_ = optarg;
      continue;
    }

    if (option == 'd') {
#if defined(PERFETTO_BUILD_WITH_ANDROID)
      dropbox_tag_ = optarg ? optarg : kDefaultDropBoxTag;
      continue;
#else
      PERFETTO_ELOG("DropBox is only supported with Android tree builds");
      return 1;
#endif
    }

    if (option == 'b') {
      background = true;
      continue;
    }
    return PrintUsage(argv[0]);
  }

  if (!trace_out_path_.empty() && !dropbox_tag_.empty()) {
    PERFETTO_ELOG(
        "Can't log to a file (--out) and DropBox (--dropbox) at the same "
        "time");
    return 1;
  }

  if (trace_config_raw.empty() ||
      (trace_out_path_.empty() && dropbox_tag_.empty())) {
    return PrintUsage(argv[0]);
  }

  perfetto::protos::TraceConfig trace_config_proto;
  PERFETTO_DLOG("Parsing TraceConfig, %zu bytes", trace_config_raw.size());
  bool parsed = trace_config_proto.ParseFromString(trace_config_raw);
  if (!parsed) {
    PERFETTO_ELOG("Could not parse TraceConfig proto from stdin");
    return 1;
  }
  trace_config_.reset(new TraceConfig());
  trace_config_->FromProto(trace_config_proto);
  trace_config_raw.clear();

  if (!OpenOutputFile())
    return 1;

  if (background) {
    PERFETTO_CHECK(daemon(0 /*nochdir*/, 0 /*noclose*/) == 0);
    PERFETTO_DLOG("Continuing in background");
  }

  consumer_endpoint_ = ConsumerIPCClient::Connect(PERFETTO_CONSUMER_SOCK_NAME,
                                                  this, &task_runner_);
  task_runner_.Run();
  return did_process_full_trace_ ? 0 : 1;
}  // namespace perfetto

void PerfettoCmd::OnConnect() {
  PERFETTO_LOG(
      "Connected to the Perfetto traced service, starting tracing for %d ms",
      trace_config_->duration_ms());
  PERFETTO_DCHECK(trace_config_);
  consumer_endpoint_->EnableTracing(*trace_config_);
  task_runner_.PostDelayedTask(std::bind(&PerfettoCmd::OnStopTraceTimer, this),
                               trace_config_->duration_ms());

  // Failsafe mechanism to avoid waiting indefinitely if the service hangs.
  task_runner_.PostDelayedTask(std::bind(&PerfettoCmd::OnTimeout, this),
                               trace_config_->duration_ms() * 2);
}

void PerfettoCmd::OnDisconnect() {
  PERFETTO_LOG("Disconnected from the Perfetto traced service");
  task_runner_.Quit();
}

void PerfettoCmd::OnStopTraceTimer() {
  PERFETTO_LOG("Timer expired, disabling tracing and collecting results");
  consumer_endpoint_->DisableTracing();
  consumer_endpoint_->ReadBuffers();
}

void PerfettoCmd::OnTimeout() {
  PERFETTO_ELOG("Timed out while waiting for trace from the service, aborting");
  task_runner_.Quit();
}

void PerfettoCmd::OnTraceData(std::vector<TracePacket> packets, bool has_more) {
  PERFETTO_DLOG("Received trace packet, has_more=%d", has_more);
  for (TracePacket& packet : packets) {
    for (const Chunk& chunk : packet) {
      uint8_t preamble[16];
      uint8_t* pos = preamble;
      pos = WriteVarInt(MakeTagLengthDelimited(1 /* field_id */), pos);
      pos = WriteVarInt(static_cast<uint32_t>(chunk.size), pos);
      fwrite(reinterpret_cast<const char*>(preamble), pos - preamble, 1,
             trace_out_stream_.get());
      fwrite(reinterpret_cast<const char*>(chunk.start), chunk.size, 1,
             trace_out_stream_.get());
    }
  }
  if (has_more)
    return;

  // Reached end of trace.
  consumer_endpoint_->FreeBuffers();
  task_runner_.Quit();

  fflush(*trace_out_stream_);
  long bytes_written = ftell(*trace_out_stream_);
  if (!dropbox_tag_.empty()) {
#if defined(PERFETTO_BUILD_WITH_ANDROID)
    android::sp<android::os::DropBoxManager> dropbox =
        new android::os::DropBoxManager();
    fseek(*trace_out_stream_, 0, SEEK_SET);
    // DropBox takes ownership of the file descriptor, so give it a duplicate.
    base::ScopedFile fd(dup(fileno(*trace_out_stream_)));
    android::binder::Status status = dropbox->addFile(
        android::String16(dropbox_tag_.c_str()), fd.release(), 0 /* flags */);
    if (!status.isOk()) {
      PERFETTO_ELOG("DropBox upload failed: %s", status.toString8().c_str());
      return;
    }
    PERFETTO_ILOG("Uploaded %ld bytes into DropBox with tag %s", bytes_written,
                  dropbox_tag_.c_str());
#endif  // defined(PERFETTO_BUILD_WITH_ANDROID)
  } else {
    PERFETTO_CHECK(
        rename(tmp_trace_out_path_.c_str(), trace_out_path_.c_str()) == 0);
    PERFETTO_ILOG("Wrote %ld bytes into %s", bytes_written,
                  trace_out_path_.c_str());
  }
  trace_out_stream_.reset();
  did_process_full_trace_ = true;
}

bool PerfettoCmd::OpenOutputFile() {
  base::ScopedFile fd;
  if (!dropbox_tag_.empty()) {
    // If we are tracing to DropBox, there's no need to make a
    // filesystem-visible temporary file.
    // TODO(skyostil): Fall back to mkstemp() + open() + unlink() for older
    // devices.
    fd.reset(open(kTempDropBoxTraceDir, O_TMPFILE | O_RDWR, 0600));
    if (!fd) {
      PERFETTO_ELOG("Could not create a temporary trace file in %s",
                    kTempDropBoxTraceDir);
      return false;
    }
  } else {
    // Otherwise create a temporary file in the directory where the final trace
    // is going to be.
    tmp_trace_out_path_ = GetDirName(trace_out_path_) + "/perfetto-traceXXXXXX";
    fd.reset(mkstemp(&tmp_trace_out_path_[0]));
  }
  trace_out_stream_.reset(fdopen(fd.release(), "wb"));
  PERFETTO_CHECK(trace_out_stream_);
  return true;
}

int __attribute__((visibility("default")))
PerfettoCmdMain(int argc, char** argv) {
  perfetto::PerfettoCmd consumer_cmd;
  return consumer_cmd.Main(argc, argv);
}

}  // namespace perfetto
<|endoftext|>


<commit_before>/** ==========================================================================
* 2011 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
* with no warranties. This code is yours to share, use and modify with no
* strings attached and no restrictions or obligations.
*
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
* ============================================================================*/

#include "g3log/logworker.hpp"
#include "g3log/logmessage.hpp"
#include "g3log/active.hpp"
#include "g3log/g3log.hpp"
#include "g3log/future.hpp"
#include "g3log/crashhandler.hpp"

#include <iostream>

namespace g3 {

   LogWorkerImpl::LogWorkerImpl() : _bg(kjellkod::Active::createActive()) { }

   void LogWorkerImpl::bgSave(g3::LogMessagePtr msgPtr) {
      std::unique_ptr<LogMessage> uniqueMsg(std::move(msgPtr.get()));

      for (auto& sink : _sinks) {
         LogMessage msg(*(uniqueMsg));
         sink->send(LogMessageMover(std::move(msg)));
      }

      if (_sinks.empty()) {
         std::string err_msg {"g3logworker has no sinks. Message: ["};
         err_msg.append(uniqueMsg.get()->toString()).append({"]\n"});
         std::cerr << err_msg;
      }
   }

   void LogWorkerImpl::bgFatal(FatalMessagePtr msgPtr) {
      // this will be the last message. Only the active logworker can receive a FATAL call so it's
      // safe to shutdown logging now
      g3::internal::shutDownLogging();

      std::string reason = msgPtr.get()->reason();
      const auto level = msgPtr.get()->_level;
      const auto fatal_id = msgPtr.get()->_signal_id;

      std::unique_ptr<LogMessage> uniqueMsg(std::move(msgPtr.get()));
      uniqueMsg->write().append("\nExiting after fatal event  (").append(uniqueMsg->level());

      // Change output in case of a fatal signal (or windows exception)
      std::string exiting = {"Fatal type: "};

      uniqueMsg->write().append("). ").append(exiting).append(" ").append(reason)
      .append("\nLog content flushed flushed sucessfully to sink\n\n");

      std::cerr << uniqueMsg->toString() << std::flush;
      for (auto& sink : _sinks) {
         LogMessage msg(*(uniqueMsg));
         sink->send(LogMessageMover(std::move(msg)));
      }

      // This clear is absolutely necessary
      // All sinks are forced to receive the fatal message above before we continue
      _sinks.clear(); // flush all queues
      internal::exitWithDefaultSignalHandler(level, fatal_id);

      // should never reach this point
      perror("g3log exited after receiving FATAL trigger. Flush message status: ");
   }

   LogWorker::~LogWorker() {
      g3::internal::shutDownLoggingForActiveOnly(this);

      // The sinks WILL automatically be cleared at exit of this destructor
      // However, the waiting below ensures that all messages until this point are taken care of
      // before any internals/LogWorkerImpl of LogWorker starts to be destroyed.
      // i.e. this avoids a race with another thread slipping through the "shutdownLogging" and calling
      // calling ::save or ::fatal through LOG/CHECK with lambda messages and "partly deconstructed LogWorkerImpl"
      //
      //   Any messages put into the queue will be OK due to:
      //  *) If it is before the wait below then they will be executed
      //  *) If it is AFTER the wait below then they will be ignored and NEVER executed
      auto bg_clear_sink_call = [this] { _impl._sinks.clear(); };
      auto token_cleared = g3::spawn_task(bg_clear_sink_call, _impl._bg.get());
      token_cleared.wait();

      // The background worker WILL be automatically cleared at the exit of the destructor
      // However, the explicitly clearing of the background worker (below) makes sure that there can
      // be no thread that manages to add another sink after the call to clear the sinks above.
      //   i.e. this manages the extremely unlikely case of another thread calling
      // addWrappedSink after the sink clear above. Normally adding of sinks should be done in main.cpp
      // and be closely coupled with the existance of the LogWorker. Sharing this adding of sinks to
      // other threads that do not know the state of LogWorker is considered a bug but it is dealt with
      // nonetheless below.
      //
      // If sinks would already have been added after the sink clear above then this reset will deal with it
      // without risking lambda execution with a partially deconstructed LogWorkerImpl
      // Calling g3::spawn_task on a nullptr Active object will not crash but return
      // a future containing an appropriate exception.
      _impl._bg.reset(nullptr);
   }

   void LogWorker::save(LogMessagePtr msg) {
      _impl._bg->send([this, msg] {_impl.bgSave(msg); });
   }

   void LogWorker::fatal(FatalMessagePtr fatal_message) {
      _impl._bg->send([this, fatal_message] {_impl.bgFatal(fatal_message); });
   }

   void LogWorker::addWrappedSink(std::shared_ptr<g3::internal::SinkWrapper> sink) {
      auto bg_addsink_call = [this, sink] {_impl._sinks.push_back(sink);};
      auto token_done = g3::spawn_task(bg_addsink_call, _impl._bg.get());
      token_done.wait();
   }

   std::unique_ptr<LogWorker> LogWorker::createLogWorker() {
      return std::unique_ptr<LogWorker>(new LogWorker);
   }

   std::unique_ptr<FileSinkHandle>LogWorker::addDefaultLogger(const std::string& log_prefix, const std::string& log_directory, const std::string& default_id) {
      return addSink(std2::make_unique<g3::FileSink>(log_prefix, log_directory, default_id), &FileSink::fileWrite);
   }

} // g3
<commit_msg>Update logworker.cpp (#103)<commit_after>/** ==========================================================================
* 2011 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
* with no warranties. This code is yours to share, use and modify with no
* strings attached and no restrictions or obligations.
*
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
* ============================================================================*/

#include "g3log/logworker.hpp"
#include "g3log/logmessage.hpp"
#include "g3log/active.hpp"
#include "g3log/g3log.hpp"
#include "g3log/future.hpp"
#include "g3log/crashhandler.hpp"

#include <iostream>

namespace g3 {

   LogWorkerImpl::LogWorkerImpl() : _bg(kjellkod::Active::createActive()) { }

   void LogWorkerImpl::bgSave(g3::LogMessagePtr msgPtr) {
      std::unique_ptr<LogMessage> uniqueMsg(std::move(msgPtr.get()));

      for (auto& sink : _sinks) {
         LogMessage msg(*(uniqueMsg));
         sink->send(LogMessageMover(std::move(msg)));
      }

      if (_sinks.empty()) {
         std::string err_msg {"g3logworker has no sinks. Message: ["};
         err_msg.append(uniqueMsg.get()->toString()).append({"]\n"});
         std::cerr << err_msg;
      }
   }

   void LogWorkerImpl::bgFatal(FatalMessagePtr msgPtr) {
      // this will be the last message. Only the active logworker can receive a FATAL call so it's
      // safe to shutdown logging now
      g3::internal::shutDownLogging();

      std::string reason = msgPtr.get()->reason();
      const auto level = msgPtr.get()->_level;
      const auto fatal_id = msgPtr.get()->_signal_id;

      std::unique_ptr<LogMessage> uniqueMsg(std::move(msgPtr.get()));
      uniqueMsg->write().append("\nExiting after fatal event  (").append(uniqueMsg->level());

      // Change output in case of a fatal signal (or windows exception)
      std::string exiting = {"Fatal type: "};

      uniqueMsg->write().append("). ").append(exiting).append(" ").append(reason)
      .append("\nLog content flushed sucessfully to sink\n\n");

      std::cerr << uniqueMsg->toString() << std::flush;
      for (auto& sink : _sinks) {
         LogMessage msg(*(uniqueMsg));
         sink->send(LogMessageMover(std::move(msg)));
      }

      // This clear is absolutely necessary
      // All sinks are forced to receive the fatal message above before we continue
      _sinks.clear(); // flush all queues
      internal::exitWithDefaultSignalHandler(level, fatal_id);

      // should never reach this point
      perror("g3log exited after receiving FATAL trigger. Flush message status: ");
   }

   LogWorker::~LogWorker() {
      g3::internal::shutDownLoggingForActiveOnly(this);

      // The sinks WILL automatically be cleared at exit of this destructor
      // However, the waiting below ensures that all messages until this point are taken care of
      // before any internals/LogWorkerImpl of LogWorker starts to be destroyed.
      // i.e. this avoids a race with another thread slipping through the "shutdownLogging" and calling
      // calling ::save or ::fatal through LOG/CHECK with lambda messages and "partly deconstructed LogWorkerImpl"
      //
      //   Any messages put into the queue will be OK due to:
      //  *) If it is before the wait below then they will be executed
      //  *) If it is AFTER the wait below then they will be ignored and NEVER executed
      auto bg_clear_sink_call = [this] { _impl._sinks.clear(); };
      auto token_cleared = g3::spawn_task(bg_clear_sink_call, _impl._bg.get());
      token_cleared.wait();

      // The background worker WILL be automatically cleared at the exit of the destructor
      // However, the explicitly clearing of the background worker (below) makes sure that there can
      // be no thread that manages to add another sink after the call to clear the sinks above.
      //   i.e. this manages the extremely unlikely case of another thread calling
      // addWrappedSink after the sink clear above. Normally adding of sinks should be done in main.cpp
      // and be closely coupled with the existance of the LogWorker. Sharing this adding of sinks to
      // other threads that do not know the state of LogWorker is considered a bug but it is dealt with
      // nonetheless below.
      //
      // If sinks would already have been added after the sink clear above then this reset will deal with it
      // without risking lambda execution with a partially deconstructed LogWorkerImpl
      // Calling g3::spawn_task on a nullptr Active object will not crash but return
      // a future containing an appropriate exception.
      _impl._bg.reset(nullptr);
   }

   void LogWorker::save(LogMessagePtr msg) {
      _impl._bg->send([this, msg] {_impl.bgSave(msg); });
   }

   void LogWorker::fatal(FatalMessagePtr fatal_message) {
      _impl._bg->send([this, fatal_message] {_impl.bgFatal(fatal_message); });
   }

   void LogWorker::addWrappedSink(std::shared_ptr<g3::internal::SinkWrapper> sink) {
      auto bg_addsink_call = [this, sink] {_impl._sinks.push_back(sink);};
      auto token_done = g3::spawn_task(bg_addsink_call, _impl._bg.get());
      token_done.wait();
   }

   std::unique_ptr<LogWorker> LogWorker::createLogWorker() {
      return std::unique_ptr<LogWorker>(new LogWorker);
   }

   std::unique_ptr<FileSinkHandle>LogWorker::addDefaultLogger(const std::string& log_prefix, const std::string& log_directory, const std::string& default_id) {
      return addSink(std2::make_unique<g3::FileSink>(log_prefix, log_directory, default_id), &FileSink::fileWrite);
   }

} // g3
<|endoftext|>


<commit_before>#include <cuda/Device.h>
#include <cuda/DeviceMath.h>

using namespace sqaod_cuda;

void testMemstore() {
    Device device;
    device.initialize(0);

    DeviceObjectAllocatorType<double> *alloc = device.deviceObjectAllocator<double>();
    DeviceMatrixType<double> mat;
    alloc->allocate(&mat, 10, 10);
    alloc->deallocate(mat);

    DeviceVectorType<double> vec;
    alloc->allocate(&vec, 10);
    alloc->deallocate(vec);

    DeviceScalarType<double> sc;
    alloc->allocate(&sc);
    alloc->deallocate(sc);

    DeviceStream *defStream = device.defaultDeviceStream();

    DeviceMatrixType<double> *tmpMat = defStream->tempDeviceMatrix<double>(10, 10);
    DeviceVectorType<double> *tmpVec = defStream->tempDeviceVector<double>(10);
    DeviceScalarType<double> *tmpSc = defStream->tempDeviceScalar<double>();

    defStream->synchronize();

    device.finalize();
}

template<class real>
void testDeviceMath() {

    namespace sq = sqaod;

    Device device;
    device.initialize(0);
    DeviceMathType<real> devMath(device);
    DeviceCopy devCopy; //(device);
    DeviceStream *devStream = device.defaultDeviceStream();

    typedef sq::MatrixType<real> HostMatrix;
    typedef DeviceMatrixType<real> DeviceMatrix;

    DeviceMatrix dA, dB, dC;

    HostMatrix hMat; // = HostMatrix::zeros(10, 10);
    devCopy(&dA, hMat);
    devMath.setToDiagonals(&dA, real(1.));
    devStream->synchronize();

    devMath.scale(&dB, 10., dA);
    devStream->synchronize();

};

int main() {
    testMemstore();
    testDeviceMath<double>();
    testDeviceMath<float>();
}
<commit_msg>simple tests added.<commit_after>#include <cuda/Device.h>
#include <cuda/DeviceMath.h>

using namespace sqaod_cuda;

void testMemstore() {
    Device device;
    device.initialize(0);

    DeviceObjectAllocatorType<double> *alloc = device.deviceObjectAllocator<double>();
    DeviceMatrixType<double> mat;
    alloc->allocate(&mat, 10, 10);
    alloc->deallocate(mat);

    DeviceVectorType<double> vec;
    alloc->allocate(&vec, 10);
    alloc->deallocate(vec);

    DeviceScalarType<double> sc;
    alloc->allocate(&sc);
    alloc->deallocate(sc);

    DeviceStream *defStream = device.defaultDeviceStream();

    DeviceMatrixType<double> *tmpMat = defStream->tempDeviceMatrix<double>(10, 10);
    DeviceVectorType<double> *tmpVec = defStream->tempDeviceVector<double>(10);
    DeviceScalarType<double> *tmpSc = defStream->tempDeviceScalar<double>();

    defStream->synchronize();

    device.finalize();
}

template<class real>
void testDeviceMath() {

    namespace sq = sqaod;

    Device device;
    device.initialize(0);
    DeviceMathType<real> devMath(device);
    DeviceCopy devCopy; //(device);
    DeviceStream *devStream = device.defaultDeviceStream();

    typedef sq::MatrixType<real> HostMatrix;
    typedef DeviceMatrixType<real> DeviceMatrix;

    DeviceMatrix dA, dB, dC;

    HostMatrix hMat = HostMatrix::zeros(10, 10);
    devCopy(&dA, hMat);
    devMath.setToDiagonals(&dA, real(1.));
    devStream->synchronize();

    devMath.scale(&dB, 10., dA);
    devStream->synchronize();

};

int main() {
    testMemstore();
    testDeviceMath<double>();
    testDeviceMath<float>();
}
<|endoftext|>


<commit_before>/******************************************************************************
 *                    _   _____   __________                                  *
 *                   | | / / _ | / __/_  __/     Visibility                   *
 *                   | |/ / __ |_\ \  / /          Across                     *
 *                   |___/_/ |_/___/ /_/       Space and Time                 *
 *                                                                            *
 * This file is part of VAST. It is subject to the license terms in the       *
 * LICENSE file found in the top-level directory of this distribution and at  *
 * http://vast.io/license. No part of VAST, including this file, may be       *
 * copied, modified, propagated, or distributed except according to the terms *
 * contained in the LICENSE file.                                             *
 ******************************************************************************/

#define SUITE test

#include "vast/error.hpp"

#include "vast/test/test.hpp"

using namespace std::string_literals;
using namespace vast;

TEST(to_string) {
  auto str = [](auto x) { return to_string(x); };
  CHECK_EQUAL(str(ec::no_error), "no error"s);
  CHECK_EQUAL(str(ec::unspecified), "unspecified"s);
  CHECK_EQUAL(str(ec::filesystem_error), "filesystem error"s);
  CHECK_EQUAL(str(ec::type_clash), "type clash"s);
  CHECK_EQUAL(str(ec::unsupported_operator), "unsupported operator"s);
  CHECK_EQUAL(str(ec::parse_error), "parse error"s);
  CHECK_EQUAL(str(ec::print_error), "print error"s);
  CHECK_EQUAL(str(ec::convert_error), "convert error"s);
  CHECK_EQUAL(str(ec::invalid_query), "invalid query"s);
  CHECK_EQUAL(str(ec::format_error), "format error"s);
  CHECK_EQUAL(str(ec::end_of_input), "end of input"s);
  CHECK_EQUAL(str(ec::version_error), "version error"s);
  CHECK_EQUAL(str(ec::syntax_error), "syntax error"s);
  CHECK_EQUAL(str(ec::invalid_table_slice_type), "invalid table slice type"s);
  CHECK_EQUAL(str(ec::invalid_synopsis_type), "invalid synopsis type"s);
  CHECK_EQUAL(str(ec::remote_node_down), "remote node down"s);
  CHECK_EQUAL(str(ec::invalid_result), "invalid result"s);
  CHECK_EQUAL(str(ec::invalid_configuration), "invalid configuration"s);
  CHECK_EQUAL(str(ec::unrecognized_option), "unrecognized option"s);
  CHECK_EQUAL(str(ec::invalid_subcommand), "invalid subcommand"s);
  CHECK_EQUAL(str(ec::missing_subcommand), "missing subcommand"s);
  CHECK_EQUAL(str(ec::no_importer), "no importer"s);
  CHECK_EQUAL(str(ec::unimplemented), "unimplemented"s);
}

TEST(render) {
  CHECK_EQUAL(render(make_error(ec::unspecified)), "!! unspecified");
  CHECK_EQUAL(render(make_error(ec::syntax_error, "msg")),
              "!! syntax error: \"msg\"");
  CHECK_EQUAL(render(make_error(ec::syntax_error, "test with", "multiple",
                                "messages")),
              "!! syntax error: \"test with\" \"multiple\" \"messages\"");
  CHECK_EQUAL(render(make_error(caf::pec::type_mismatch, "ttt")),
              "!! type_mismatch: ([(\"argument\", \"ttt\")])");
  CHECK_EQUAL(render(make_error(caf::sec::unexpected_message, "msg")),
              "!! unexpected_message: (\"msg\")");
  CHECK_EQUAL(render(caf::error(255, caf::atom("foobar"),
                                caf::make_message(255, "msg"))),
              "!! foobar: (255, \"msg\")");
}
<commit_msg>Fix unit test expectation values<commit_after>/******************************************************************************
 *                    _   _____   __________                                  *
 *                   | | / / _ | / __/_  __/     Visibility                   *
 *                   | |/ / __ |_\ \  / /          Across                     *
 *                   |___/_/ |_/___/ /_/       Space and Time                 *
 *                                                                            *
 * This file is part of VAST. It is subject to the license terms in the       *
 * LICENSE file found in the top-level directory of this distribution and at  *
 * http://vast.io/license. No part of VAST, including this file, may be       *
 * copied, modified, propagated, or distributed except according to the terms *
 * contained in the LICENSE file.                                             *
 ******************************************************************************/

#define SUITE test

#include "vast/error.hpp"

#include "vast/test/test.hpp"

using namespace std::string_literals;
using namespace vast;

TEST(to_string) {
  auto str = [](auto x) { return to_string(x); };
  CHECK_EQUAL(str(ec::no_error), "no_error"s);
  CHECK_EQUAL(str(ec::unspecified), "unspecified"s);
  CHECK_EQUAL(str(ec::filesystem_error), "filesystem_error"s);
  CHECK_EQUAL(str(ec::type_clash), "type_clash"s);
  CHECK_EQUAL(str(ec::unsupported_operator), "unsupported_operator"s);
  CHECK_EQUAL(str(ec::parse_error), "parse_error"s);
  CHECK_EQUAL(str(ec::print_error), "print_error"s);
  CHECK_EQUAL(str(ec::convert_error), "convert_error"s);
  CHECK_EQUAL(str(ec::invalid_query), "invalid_query"s);
  CHECK_EQUAL(str(ec::format_error), "format_error"s);
  CHECK_EQUAL(str(ec::end_of_input), "end_of_input"s);
  CHECK_EQUAL(str(ec::version_error), "version_error"s);
  CHECK_EQUAL(str(ec::syntax_error), "syntax_error"s);
  CHECK_EQUAL(str(ec::invalid_table_slice_type), "invalid_table_slice_type"s);
  CHECK_EQUAL(str(ec::invalid_synopsis_type), "invalid_synopsis_type"s);
  CHECK_EQUAL(str(ec::remote_node_down), "remote_node_down"s);
  CHECK_EQUAL(str(ec::invalid_result), "invalid_result"s);
  CHECK_EQUAL(str(ec::invalid_configuration), "invalid_configuration"s);
  CHECK_EQUAL(str(ec::unrecognized_option), "unrecognized_option"s);
  CHECK_EQUAL(str(ec::invalid_subcommand), "invalid_subcommand"s);
  CHECK_EQUAL(str(ec::missing_subcommand), "missing_subcommand"s);
  CHECK_EQUAL(str(ec::no_importer), "no_importer"s);
  CHECK_EQUAL(str(ec::unimplemented), "unimplemented"s);
}

TEST(render) {
  CHECK_EQUAL(render(make_error(ec::unspecified)), "!! unspecified");
  CHECK_EQUAL(render(make_error(ec::syntax_error, "msg")),
              "!! syntax_error: \"msg\"");
  CHECK_EQUAL(render(make_error(ec::syntax_error, "test with", "multiple",
                                "messages")),
              "!! syntax_error: \"test with\" \"multiple\" \"messages\"");
  CHECK_EQUAL(render(make_error(caf::pec::type_mismatch, "ttt")),
              "!! type_mismatch: [(\"argument\", \"ttt\")]");
  CHECK_EQUAL(render(make_error(caf::sec::unexpected_message, "msg")),
              "!! unexpected_message: \"msg\"");
  CHECK_EQUAL(render(caf::error(255, caf::atom("foobar"),
                                caf::make_message(255, "msg"))),
              "!! foobar: 255 \"msg\"");
}
<|endoftext|>


<commit_before>// Copyright (c) 2008, Google Inc.
// 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 Google Inc. 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.

#include "config.h"
#include <windows.h>
#include <vector>

#include "ChromiumBridge.h"
#include "Font.h"
#include "GlyphPageTreeNode.h"
#include "SimpleFontData.h"
#include "UniscribeHelperTextRun.h"

#include "base/win_util.h"

namespace WebCore
{

// Fills one page of font data pointers with NULL to indicate that there
// are no glyphs for the characters.
static void FillEmptyGlyphs(GlyphPage* page) {
  for (int i = 0; i < GlyphPage::size; ++i)
    page->setGlyphDataForIndex(i, NULL, NULL);
}

// Lazily initializes space glyph
static Glyph InitSpaceGlyph(HDC dc, Glyph* space_glyph) {
    if (*space_glyph)
        return *space_glyph;
    static wchar_t space = ' ';
    GetGlyphIndices(dc, &space, 1, space_glyph, 0);
    return *space_glyph;
}

// Fills a page of glyphs in the Basic Multilingual Plane (<= U+FFFF). We
// can use the standard Windows GDI functions here. The input buffer size is
// assumed to be GlyphPage::size. Returns true if any glyphs were found.
static bool FillBMPGlyphs(UChar* buffer,
                          GlyphPage* page,
                          const SimpleFontData* fontData,
                          bool recurse)
{
    HDC dc = GetDC((HWND)0);
    HGDIOBJ old_font = SelectObject(dc, fontData->m_font.hfont());

    TEXTMETRIC tm = {0};
    if (!GetTextMetrics(dc, &tm)) {
      SelectObject(dc, old_font);
      ReleaseDC(0, dc);

      if (recurse) {
        if (ChromiumBridge::ensureFontLoaded(fontData->m_font.hfont())) {
          return FillBMPGlyphs(buffer, page, fontData, false);
        } else {
          FillEmptyGlyphs(page);
          return false;
        }
      } else {
        // TODO(nsylvain): This should never happen. We want to crash the
        // process and receive a crash dump. We should revisit this code later.
        // See bug 1136944.
        ASSERT_NOT_REACHED();
        FillEmptyGlyphs(page);
        return false;
      }
    }

    // NOTE(hbono): GetGlyphIndices() sets each item of localGlyphBuffer[]
    // with the one of the values listed below.
    //  * With the GGI_MARK_NONEXISTING_GLYPHS flag
    //    + If the font has a glyph available for the character,
    //      localGlyphBuffer[i] > 0x0.
    //    + If the font does not have glyphs available for the character,
    //      localGlyphBuffer[i] = 0x1F (TrueType Collection?) or
    //                            0xFFFF (OpenType?).
    //  * Without the GGI_MARK_NONEXISTING_GLYPHS flag
    //    + If the font has a glyph available for the character,
    //      localGlyphBuffer[i] > 0x0.
    //    + If the font does not have glyphs available for the character,
    //      localGlyphBuffer[i] = 0x80.
    //      (Windows automatically assigns the glyph for a box character to
    //      prevent ExtTextOut() from returning errors.)
    // To avoid from hurting the rendering performance, this code just
    // tells WebKit whether or not the all glyph indices for the given
    // characters are 0x80 (i.e. a possibly-invalid glyph) and let it
    // use alternative fonts for the characters.
    // Although this may cause a problem, it seems to work fine as far as I
    // have tested. (Obviously, I need more tests.)
    WORD localGlyphBuffer[GlyphPage::size];

    // NOTE(jnd). I find some Chinese characters can not be correctly displayed
    // when call GetGlyphIndices without flag GGI_MARK_NONEXISTING_GLYPHS,
    // because the corresponding glyph index is set as 0x20 when current font
    // does not have glyphs available for the character. According a blog post
    // http://blogs.msdn.com/michkap/archive/2006/06/28/649791.aspx
    // I think we should switch to the way about calling GetGlyphIndices with
    // flag GGI_MARK_NONEXISTING_GLYPHS, it should be OK according the
    // description of MSDN.
    // Also according to Jungshik and Hironori's suggestion and modification
    // we treat turetype and raster Font as different way when windows version
    // is less than Vista.
    GetGlyphIndices(dc, buffer, GlyphPage::size, localGlyphBuffer,
                    GGI_MARK_NONEXISTING_GLYPHS);

    // Copy the output to the GlyphPage
    bool have_glyphs = false;
    int invalid_glyph = 0xFFFF;
    if (win_util::GetWinVersion() < win_util::WINVERSION_VISTA &&
        !(tm.tmPitchAndFamily & TMPF_TRUETYPE))
      invalid_glyph = 0x1F;

    Glyph space_glyph = 0;  // Glyph for a space. Lazily filled.

    for (unsigned i = 0; i < GlyphPage::size; i++) {
        UChar c = buffer[i];
        Glyph glyph = localGlyphBuffer[i];
        const SimpleFontData* glyphFontData = fontData;
        // When this character should be a space, we ignore whatever the font
        // says and use a space. Otherwise, if fonts don't map one of these
        // space or zero width glyphs, we will get a box.
        if (Font::treatAsSpace(c)) {
            // Hard code the glyph indices for characters that should be
            // treated like spaces.
            glyph = InitSpaceGlyph(dc, &space_glyph);
        // TODO(dglazkov): change Font::treatAsZeroWidthSpace to use
        // u_hasBinaryProperty, per jungshik's comment here:
        // https://bugs.webkit.org/show_bug.cgi?id=20237#c6.
        // Then the additional OR won't be necessary.
        } else if (Font::treatAsZeroWidthSpace(c) || c == 0x200B) {
            glyph = InitSpaceGlyph(dc, &space_glyph);
            glyphFontData = fontData->zeroWidthFontData();
        } else if (glyph == invalid_glyph) {
            // WebKit expects both the glyph index and FontData
            // pointer to be NULL if the glyph is not present
            glyph = 0;
            glyphFontData = 0;
        } else {
            if (SimpleFontData::isCJKCodePoint(c))
                glyphFontData = fontData->cjkWidthFontData();
            have_glyphs = true;
        }
        page->setGlyphDataForCharacter(i, glyph, glyphFontData);
    }

    SelectObject(dc, old_font);
    ReleaseDC(0, dc);
    return have_glyphs;
}

// For non-BMP characters, each is two words (UTF-16) and the input buffer size
// is (GlyphPage::size * 1). Since GDI doesn't know how to handle non-BMP
// characters, we must use Uniscribe to tell us the glyph indices.
//
// We don't want to call this in the case of "regular" characters since some
// fonts may not have the correct combining rules for accents. See the notes
// at the bottom of ScriptGetCMap. We can't use ScriptGetCMap, though, since
// it doesn't seem to support UTF-16, despite what this blog post says:
//   http://blogs.msdn.com/michkap/archive/2006/06/29/650680.aspx
//
// So we fire up the full Uniscribe doohicky, give it our string, and it will
// correctly handle the UTF-16 for us. The hard part is taking this and getting
// the glyph indices back out that correspond to the correct input characters,
// since they may be missing.
//
// Returns true if any glyphs were found.
static bool FillNonBMPGlyphs(UChar* buffer,
                             GlyphPage* page,
                             const SimpleFontData* fontData)
{
    bool have_glyphs = false;

    UniscribeHelperTextRun state(buffer, GlyphPage::size * 2, false,
                                 fontData->m_font.hfont(),
                                 fontData->m_font.scriptCache(),
                                 fontData->m_font.scriptFontProperties());
    state.setInhibitLigate(true);
    state.Init();

    for (unsigned i = 0; i < GlyphPage::size; i++) {
        WORD glyph = state.FirstGlyphForCharacter(i);
        if (glyph) {
            have_glyphs = true;
            page->setGlyphDataForIndex(i, glyph, fontData);
        } else {
            // Clear both glyph and fontData fields.
            page->setGlyphDataForIndex(i, 0, 0);
        }
    }
    return have_glyphs;
}

// We're supposed to return true if there are any glyphs in this page in our
// font, false if there are none.
bool GlyphPage::fill(unsigned offset, unsigned length, UChar* characterBuffer, unsigned bufferLength, const SimpleFontData* fontData)
{
    // This function's parameters are kind of stupid. We always fill this page,
    // which is a fixed size. The source character indices are in the given
    // input buffer. For non-BMP characters each character will be represented
    // by a surrogate pair (two characters), so the input bufferLength will be
    // twice as big, even though the output size is the same.
    //
    // We have to handle BMP and non-BMP characters differently anyway...
    if (bufferLength == GlyphPage::size) {
        return FillBMPGlyphs(characterBuffer, this, fontData, true);
    } else if (bufferLength == GlyphPage::size * 2) {
        return FillNonBMPGlyphs(characterBuffer, this, fontData);
    } else {
        // TODO: http://b/1007391 make use of offset and length
        return false;
    }
}

}
<commit_msg>A fix for Issue 5139 "some plane-1 characters are not displayed correctly". The WebCore::UnscribeHelper::FirstGlyphForCharacter() requires a UChar offset. So, we need to increase TWO for a surrogate pair.<commit_after>// Copyright (c) 2008, Google Inc.
// 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 Google Inc. 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.

#include "config.h"
#include <windows.h>
#include <vector>

#include "ChromiumBridge.h"
#include "Font.h"
#include "GlyphPageTreeNode.h"
#include "SimpleFontData.h"
#include "UniscribeHelperTextRun.h"

#include "base/win_util.h"

namespace WebCore
{

// Fills one page of font data pointers with NULL to indicate that there
// are no glyphs for the characters.
static void FillEmptyGlyphs(GlyphPage* page) {
  for (int i = 0; i < GlyphPage::size; ++i)
    page->setGlyphDataForIndex(i, NULL, NULL);
}

// Lazily initializes space glyph
static Glyph InitSpaceGlyph(HDC dc, Glyph* space_glyph) {
    if (*space_glyph)
        return *space_glyph;
    static wchar_t space = ' ';
    GetGlyphIndices(dc, &space, 1, space_glyph, 0);
    return *space_glyph;
}

// Fills a page of glyphs in the Basic Multilingual Plane (<= U+FFFF). We
// can use the standard Windows GDI functions here. The input buffer size is
// assumed to be GlyphPage::size. Returns true if any glyphs were found.
static bool FillBMPGlyphs(UChar* buffer,
                          GlyphPage* page,
                          const SimpleFontData* fontData,
                          bool recurse)
{
    HDC dc = GetDC((HWND)0);
    HGDIOBJ old_font = SelectObject(dc, fontData->m_font.hfont());

    TEXTMETRIC tm = {0};
    if (!GetTextMetrics(dc, &tm)) {
      SelectObject(dc, old_font);
      ReleaseDC(0, dc);

      if (recurse) {
        if (ChromiumBridge::ensureFontLoaded(fontData->m_font.hfont())) {
          return FillBMPGlyphs(buffer, page, fontData, false);
        } else {
          FillEmptyGlyphs(page);
          return false;
        }
      } else {
        // TODO(nsylvain): This should never happen. We want to crash the
        // process and receive a crash dump. We should revisit this code later.
        // See bug 1136944.
        ASSERT_NOT_REACHED();
        FillEmptyGlyphs(page);
        return false;
      }
    }

    // NOTE(hbono): GetGlyphIndices() sets each item of localGlyphBuffer[]
    // with the one of the values listed below.
    //  * With the GGI_MARK_NONEXISTING_GLYPHS flag
    //    + If the font has a glyph available for the character,
    //      localGlyphBuffer[i] > 0x0.
    //    + If the font does not have glyphs available for the character,
    //      localGlyphBuffer[i] = 0x1F (TrueType Collection?) or
    //                            0xFFFF (OpenType?).
    //  * Without the GGI_MARK_NONEXISTING_GLYPHS flag
    //    + If the font has a glyph available for the character,
    //      localGlyphBuffer[i] > 0x0.
    //    + If the font does not have glyphs available for the character,
    //      localGlyphBuffer[i] = 0x80.
    //      (Windows automatically assigns the glyph for a box character to
    //      prevent ExtTextOut() from returning errors.)
    // To avoid from hurting the rendering performance, this code just
    // tells WebKit whether or not the all glyph indices for the given
    // characters are 0x80 (i.e. a possibly-invalid glyph) and let it
    // use alternative fonts for the characters.
    // Although this may cause a problem, it seems to work fine as far as I
    // have tested. (Obviously, I need more tests.)
    WORD localGlyphBuffer[GlyphPage::size];

    // NOTE(jnd). I find some Chinese characters can not be correctly displayed
    // when call GetGlyphIndices without flag GGI_MARK_NONEXISTING_GLYPHS,
    // because the corresponding glyph index is set as 0x20 when current font
    // does not have glyphs available for the character. According a blog post
    // http://blogs.msdn.com/michkap/archive/2006/06/28/649791.aspx
    // I think we should switch to the way about calling GetGlyphIndices with
    // flag GGI_MARK_NONEXISTING_GLYPHS, it should be OK according the
    // description of MSDN.
    // Also according to Jungshik and Hironori's suggestion and modification
    // we treat turetype and raster Font as different way when windows version
    // is less than Vista.
    GetGlyphIndices(dc, buffer, GlyphPage::size, localGlyphBuffer,
                    GGI_MARK_NONEXISTING_GLYPHS);

    // Copy the output to the GlyphPage
    bool have_glyphs = false;
    int invalid_glyph = 0xFFFF;
    if (win_util::GetWinVersion() < win_util::WINVERSION_VISTA &&
        !(tm.tmPitchAndFamily & TMPF_TRUETYPE))
      invalid_glyph = 0x1F;

    Glyph space_glyph = 0;  // Glyph for a space. Lazily filled.

    for (unsigned i = 0; i < GlyphPage::size; i++) {
        UChar c = buffer[i];
        Glyph glyph = localGlyphBuffer[i];
        const SimpleFontData* glyphFontData = fontData;
        // When this character should be a space, we ignore whatever the font
        // says and use a space. Otherwise, if fonts don't map one of these
        // space or zero width glyphs, we will get a box.
        if (Font::treatAsSpace(c)) {
            // Hard code the glyph indices for characters that should be
            // treated like spaces.
            glyph = InitSpaceGlyph(dc, &space_glyph);
        // TODO(dglazkov): change Font::treatAsZeroWidthSpace to use
        // u_hasBinaryProperty, per jungshik's comment here:
        // https://bugs.webkit.org/show_bug.cgi?id=20237#c6.
        // Then the additional OR won't be necessary.
        } else if (Font::treatAsZeroWidthSpace(c) || c == 0x200B) {
            glyph = InitSpaceGlyph(dc, &space_glyph);
            glyphFontData = fontData->zeroWidthFontData();
        } else if (glyph == invalid_glyph) {
            // WebKit expects both the glyph index and FontData
            // pointer to be NULL if the glyph is not present
            glyph = 0;
            glyphFontData = 0;
        } else {
            if (SimpleFontData::isCJKCodePoint(c))
                glyphFontData = fontData->cjkWidthFontData();
            have_glyphs = true;
        }
        page->setGlyphDataForCharacter(i, glyph, glyphFontData);
    }

    SelectObject(dc, old_font);
    ReleaseDC(0, dc);
    return have_glyphs;
}

// For non-BMP characters, each is two words (UTF-16) and the input buffer size
// is (GlyphPage::size * 1). Since GDI doesn't know how to handle non-BMP
// characters, we must use Uniscribe to tell us the glyph indices.
//
// We don't want to call this in the case of "regular" characters since some
// fonts may not have the correct combining rules for accents. See the notes
// at the bottom of ScriptGetCMap. We can't use ScriptGetCMap, though, since
// it doesn't seem to support UTF-16, despite what this blog post says:
//   http://blogs.msdn.com/michkap/archive/2006/06/29/650680.aspx
//
// So we fire up the full Uniscribe doohicky, give it our string, and it will
// correctly handle the UTF-16 for us. The hard part is taking this and getting
// the glyph indices back out that correspond to the correct input characters,
// since they may be missing.
//
// Returns true if any glyphs were found.
static bool FillNonBMPGlyphs(UChar* buffer,
                             GlyphPage* page,
                             const SimpleFontData* fontData)
{
    bool have_glyphs = false;

    UniscribeHelperTextRun state(buffer, GlyphPage::size * 2, false,
                                 fontData->m_font.hfont(),
                                 fontData->m_font.scriptCache(),
                                 fontData->m_font.scriptFontProperties());
    state.setInhibitLigate(true);
    state.Init();

    for (unsigned i = 0; i < GlyphPage::size; i++) {
        // Each character in this input buffer is a surrogate pair, which
        // consists of two UChars. So, the offset for its i-th character is
        // (i * 2).
        WORD glyph = state.FirstGlyphForCharacter(i * 2);
        if (glyph) {
            have_glyphs = true;
            page->setGlyphDataForIndex(i, glyph, fontData);
        } else {
            // Clear both glyph and fontData fields.
            page->setGlyphDataForIndex(i, 0, 0);
        }
    }
    return have_glyphs;
}

// We're supposed to return true if there are any glyphs in this page in our
// font, false if there are none.
bool GlyphPage::fill(unsigned offset, unsigned length, UChar* characterBuffer, unsigned bufferLength, const SimpleFontData* fontData)
{
    // This function's parameters are kind of stupid. We always fill this page,
    // which is a fixed size. The source character indices are in the given
    // input buffer. For non-BMP characters each character will be represented
    // by a surrogate pair (two characters), so the input bufferLength will be
    // twice as big, even though the output size is the same.
    //
    // We have to handle BMP and non-BMP characters differently anyway...
    if (bufferLength == GlyphPage::size) {
        return FillBMPGlyphs(characterBuffer, this, fontData, true);
    } else if (bufferLength == GlyphPage::size * 2) {
        return FillNonBMPGlyphs(characterBuffer, this, fontData);
    } else {
        // TODO: http://b/1007391 make use of offset and length
        return false;
    }
}

}
<|endoftext|>


<commit_before>/*
 * Copyright 2014-present Facebook, Inc.
 *
 * 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.
 */
#define __STDC_FORMAT_MACROS

#include <thrift/perf/cpp/ClientLoadConfig.h>

#include <thrift/lib/cpp/test/loadgen/RNG.h>

using namespace boost;

DEFINE_string(server, "127.0.0.1", "Name/IP address of the server to test");
DEFINE_int32(port, 1234, "server port");
DEFINE_bool(framed, true, "use TFramedTransport");
DEFINE_bool(header, false, "use THeaderProtocol");
DEFINE_bool(async, false, "Use async client");
DEFINE_bool(ssl, false, "Use SSL");
DEFINE_bool(single_host, false, "Use Single Hosts option with Service Router");
DEFINE_string(key, "", "client SSL private key file");
DEFINE_string(cert, "", "client SSL certificate file");
DEFINE_string(trusted_ca_list, "", "file pointing to a trusted CA or list");
DEFINE_bool(use_tickets, true, "Use SSL session tickets on the client?");
DEFINE_bool(use_ssl_tfo, true, "Use TFO for SSL connections");
/*
 * Please refer to the online OpenSSL manual for the format of the string of
 * ciphers. The default cipher list is "ALL:!aNULL:!eNULL", which means all
 * ciphers except ones with no authentication (aNULL) or no encryption (eNULL).
 */
DEFINE_string(ciphers, "", "cipher suites supported by the client");

DEFINE_int32(num_threads, 5, "number of threads");
DEFINE_int64(qps, 0,
             "desired # of queries per second (0 for infinite)");
DEFINE_int32(ops_per_conn, 1000,
             "number of operations to issue before opening a new connection");
DEFINE_int32(async_clients, 1,
             "number of simultaneous connections per thread");
DEFINE_int32(async_ops, 10,
             "number of oustanding async ops per connection");
DEFINE_bool(zlib, false, "use zlib compression");

// Relative weights for operation frequencies
DEFINE_int32(weight_noop, 0, "frequency weight for noop()");
DEFINE_int32(weight_oneway_noop, 0, "frequency weight for onewayNoop()");
DEFINE_int32(weight_async_noop, 0, "frequency weight for asyncNoop()");
DEFINE_int32(weight_sleep, 0, "frequency weight for sleep()");
DEFINE_int32(weight_oneway_sleep, 0, "frequency weight for onewaySleep()");
DEFINE_int32(weight_burn, 0, "frequency weight for burn()");
DEFINE_int32(weight_oneway_burn, 0, "frequency weight for onewayBurn()");
DEFINE_int32(weight_bad_sleep, 0, "frequency weight for badSleep()");
DEFINE_int32(weight_bad_burn, 0, "frequency weight for badBurn()");
DEFINE_int32(weight_throw_error, 0, "frequency weight for throwError()");
DEFINE_int32(weight_throw_unexpected, 0,
             "frequency weight for throwUnexpected()");
DEFINE_int32(weight_oneway_throw, 0, "frequency weight for onewayThrow()");
DEFINE_int32(weight_send, 0, "frequency weight for send()");
DEFINE_int32(weight_oneway_send, 0, "frequency weight for onewaySend()");
DEFINE_int32(weight_recv, 0, "frequency weight for recv()");
DEFINE_int32(weight_sendrecv, 0, "frequency weight for sendrecv()");
DEFINE_int32(weight_echo, 0, "frequency weight for echo()");
DEFINE_int32(weight_add, 0, "frequency weight for add()");
DEFINE_int32(weight_large_container, 0,
             "frequency weight for large_container()");
DEFINE_int32(weight_iter_all_fields, 0,
            "frequency weight for iter_all_fields()");

// Controls for how long sleep and burn operations should take
DEFINE_double(sleep_avg, 5000.0,
              "average # of microseconds for sleep operations");
DEFINE_double(sleep_sigma, -1.0,
              "log-normal sigma parameter for sleep duration");
DEFINE_double(burn_avg, 5000.0,
              "average # of microseconds for sleep operations");
DEFINE_double(burn_sigma, -1.0,
              "log-normal sigma parameter for sleep duration");
DEFINE_double(send_size_avg, 16384.0,
              "average # of bytes for send operations");
DEFINE_double(send_size_sigma, -1.0,
              "log-normal sigma parameter for send size");
DEFINE_double(recv_size_avg, 16384.0,
              "average # of bytes for receive operations");
DEFINE_double(recv_size_sigma, -1.0,
              "log-normal sigma parameter for receive size");
DEFINE_double(container_size_avg, 100.0,
              "average # of structs to put in a container");
DEFINE_double(container_size_sigma, -1.0,
              "log-normal sigma parameter for container size");
DEFINE_double(struct_field_size_avg, 64.0,
              "average length of field in BigStructs");
DEFINE_double(struct_field_size_sigma, -1.0,
              "log-normal sigma parameter for struct field size");

namespace apache { namespace thrift { namespace test {

ClientLoadConfig::ClientLoadConfig()
  : WeightedLoadConfig(NUM_OPS) {
  setOpInfo(OP_NOOP, "noop()", FLAGS_weight_noop);
  setOpInfo(OP_ONEWAY_NOOP, "onewayNoop()", FLAGS_weight_oneway_noop);
  setOpInfo(OP_ASYNC_NOOP, "asyncNoop()", FLAGS_weight_async_noop);
  setOpInfo(OP_SLEEP, "sleep()", FLAGS_weight_sleep);
  setOpInfo(OP_ONEWAY_SLEEP, "onewaySleep()", FLAGS_weight_oneway_sleep);
  setOpInfo(OP_BURN, "burn()", FLAGS_weight_burn);
  setOpInfo(OP_ONEWAY_BURN, "onewayBurn()", FLAGS_weight_oneway_burn);
  setOpInfo(OP_BAD_SLEEP, "badSleep()", FLAGS_weight_bad_sleep);
  setOpInfo(OP_BAD_BURN, "badBurn()", FLAGS_weight_bad_burn);
  setOpInfo(OP_THROW_ERROR, "throwError()", FLAGS_weight_throw_error);
  setOpInfo(OP_THROW_UNEXPECTED, "throwUnexpected()",
            FLAGS_weight_throw_unexpected);
  setOpInfo(OP_ONEWAY_THROW, "onewayThrow()", FLAGS_weight_oneway_throw);
  setOpInfo(OP_SEND, "send()", FLAGS_weight_send);
  setOpInfo(OP_ONEWAY_SEND, "onewaySleep()", FLAGS_weight_oneway_send);
  setOpInfo(OP_RECV, "recv()", FLAGS_weight_recv);
  setOpInfo(OP_SENDRECV, "sendrecv()", FLAGS_weight_sendrecv);
  setOpInfo(OP_ECHO, "echo()", FLAGS_weight_echo);
  setOpInfo(OP_ADD, "add()", FLAGS_weight_add);
  setOpInfo(OP_LARGE_CONTAINER, "large_container()",
            FLAGS_weight_large_container);
  setOpInfo(OP_ITER_ALL_FIELDS, "iter_all_fields()",
            FLAGS_weight_iter_all_fields);

  // Look up the hostname, and cache the result so we don't have to perform a
  // resolution for each connection attempt.
  address_.setFromHostPort(FLAGS_server.c_str(), FLAGS_port);
  if (FLAGS_server == "127.0.0.1") {
    char hostname[HOST_NAME_MAX + 1];
    PCHECK(gethostname(hostname, sizeof(hostname)));
    addressHostname_ = hostname;
  } else {
    struct addrinfo hints, *res, *res0;
    char hostname[NI_MAXHOST] = {};

    memset(&hints, 0, sizeof(hints));
    hints.ai_family = PF_UNSPEC;
    hints.ai_flags = AI_NUMERICHOST;
    PCHECK(
        !getaddrinfo(address_.getAddressStr().c_str(), nullptr, &hints, &res0));

    for (res = res0; res; res = res->ai_next) {
      if (0 ==
          getnameinfo(
              res->ai_addr,
              res->ai_addrlen,
              hostname,
              NI_MAXHOST,
              nullptr,
              0,
              NI_NAMEREQD)) {
        break;
      }
    }
    freeaddrinfo(res0);
    addressHostname_ = hostname;
  }

  // TODO: verify that at least one weight is non-zero
  // TODO: it would be nice to be able to load from a config file
}

uint32_t ClientLoadConfig::pickOpsPerConnection() {
  return FLAGS_ops_per_conn;
}

uint32_t ClientLoadConfig::getAsyncClients() const {
  return FLAGS_async_clients;
}

uint32_t ClientLoadConfig::getAsyncOpsPerClient() const {
  return FLAGS_async_ops;
}

uint32_t ClientLoadConfig::getNumWorkerThreads() const {
  return FLAGS_num_threads;
}

uint64_t ClientLoadConfig::getDesiredQPS() const {
  if (FLAGS_qps <= 0) {
    return 0;
  } else {
    return FLAGS_qps;
  }
}

uint32_t ClientLoadConfig::pickSleepUsec() {
  return pickLogNormal(FLAGS_sleep_avg, FLAGS_sleep_sigma);
}

uint32_t ClientLoadConfig::pickBurnUsec() {
  return pickLogNormal(FLAGS_burn_avg, FLAGS_burn_sigma);
}

uint32_t ClientLoadConfig::pickSendSize() {
  return pickLogNormal(FLAGS_send_size_avg, FLAGS_send_size_sigma);
}

uint32_t ClientLoadConfig::pickRecvSize() {
  return pickLogNormal(FLAGS_recv_size_avg, FLAGS_recv_size_sigma);
}

uint32_t ClientLoadConfig::pickContainerSize() {
  return pickLogNormal(FLAGS_container_size_avg, FLAGS_container_size_sigma);
}

uint32_t ClientLoadConfig::pickStructFieldSize() {
  return pickLogNormal(
    FLAGS_struct_field_size_avg,
    FLAGS_struct_field_size_sigma);
}

bool ClientLoadConfig::useFramedTransport() const {
  return FLAGS_framed;
}

bool ClientLoadConfig::useHeaderProtocol() const {
  return FLAGS_header;
}

bool ClientLoadConfig::useAsync() const {
  return FLAGS_async;
}

bool ClientLoadConfig::useSSL() const {
  return FLAGS_ssl;
}

bool ClientLoadConfig::useSSLTFO() const {
  return FLAGS_use_ssl_tfo;
}

bool ClientLoadConfig::useSingleHost() const {
  return FLAGS_single_host;
}

bool ClientLoadConfig::zlib() const {
  return FLAGS_zlib;
}

std::string ClientLoadConfig::server() const {
  return FLAGS_server;
}

uint32_t ClientLoadConfig::port() const {
  return FLAGS_port;
}

std::string ClientLoadConfig::key() const {
  return FLAGS_key;
}

std::string ClientLoadConfig::cert() const {
  return FLAGS_cert;
}

std::string ClientLoadConfig::trustedCAList() const {
  return FLAGS_trusted_ca_list;
}

std::string ClientLoadConfig::ciphers() const {
  return FLAGS_ciphers;
}

bool ClientLoadConfig::useTickets() const {
  return FLAGS_use_tickets;
}

uint32_t ClientLoadConfig::pickLogNormal(double mean, double sigma) {
  return static_cast<uint32_t>(loadgen::RNG::getLogNormal(mean, sigma));
}

}}} // apache::thrift::test
<commit_msg>(trivial) perf/loadgen - fix broken PCHECK()<commit_after>/*
 * Copyright 2014-present Facebook, Inc.
 *
 * 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.
 */
#define __STDC_FORMAT_MACROS

#include <thrift/perf/cpp/ClientLoadConfig.h>

#include <thrift/lib/cpp/test/loadgen/RNG.h>

using namespace boost;

DEFINE_string(server, "127.0.0.1", "Name/IP address of the server to test");
DEFINE_int32(port, 1234, "server port");
DEFINE_bool(framed, true, "use TFramedTransport");
DEFINE_bool(header, false, "use THeaderProtocol");
DEFINE_bool(async, false, "Use async client");
DEFINE_bool(ssl, false, "Use SSL");
DEFINE_bool(single_host, false, "Use Single Hosts option with Service Router");
DEFINE_string(key, "", "client SSL private key file");
DEFINE_string(cert, "", "client SSL certificate file");
DEFINE_string(trusted_ca_list, "", "file pointing to a trusted CA or list");
DEFINE_bool(use_tickets, true, "Use SSL session tickets on the client?");
DEFINE_bool(use_ssl_tfo, true, "Use TFO for SSL connections");
/*
 * Please refer to the online OpenSSL manual for the format of the string of
 * ciphers. The default cipher list is "ALL:!aNULL:!eNULL", which means all
 * ciphers except ones with no authentication (aNULL) or no encryption (eNULL).
 */
DEFINE_string(ciphers, "", "cipher suites supported by the client");

DEFINE_int32(num_threads, 5, "number of threads");
DEFINE_int64(qps, 0,
             "desired # of queries per second (0 for infinite)");
DEFINE_int32(ops_per_conn, 1000,
             "number of operations to issue before opening a new connection");
DEFINE_int32(async_clients, 1,
             "number of simultaneous connections per thread");
DEFINE_int32(async_ops, 10,
             "number of oustanding async ops per connection");
DEFINE_bool(zlib, false, "use zlib compression");

// Relative weights for operation frequencies
DEFINE_int32(weight_noop, 0, "frequency weight for noop()");
DEFINE_int32(weight_oneway_noop, 0, "frequency weight for onewayNoop()");
DEFINE_int32(weight_async_noop, 0, "frequency weight for asyncNoop()");
DEFINE_int32(weight_sleep, 0, "frequency weight for sleep()");
DEFINE_int32(weight_oneway_sleep, 0, "frequency weight for onewaySleep()");
DEFINE_int32(weight_burn, 0, "frequency weight for burn()");
DEFINE_int32(weight_oneway_burn, 0, "frequency weight for onewayBurn()");
DEFINE_int32(weight_bad_sleep, 0, "frequency weight for badSleep()");
DEFINE_int32(weight_bad_burn, 0, "frequency weight for badBurn()");
DEFINE_int32(weight_throw_error, 0, "frequency weight for throwError()");
DEFINE_int32(weight_throw_unexpected, 0,
             "frequency weight for throwUnexpected()");
DEFINE_int32(weight_oneway_throw, 0, "frequency weight for onewayThrow()");
DEFINE_int32(weight_send, 0, "frequency weight for send()");
DEFINE_int32(weight_oneway_send, 0, "frequency weight for onewaySend()");
DEFINE_int32(weight_recv, 0, "frequency weight for recv()");
DEFINE_int32(weight_sendrecv, 0, "frequency weight for sendrecv()");
DEFINE_int32(weight_echo, 0, "frequency weight for echo()");
DEFINE_int32(weight_add, 0, "frequency weight for add()");
DEFINE_int32(weight_large_container, 0,
             "frequency weight for large_container()");
DEFINE_int32(weight_iter_all_fields, 0,
            "frequency weight for iter_all_fields()");

// Controls for how long sleep and burn operations should take
DEFINE_double(sleep_avg, 5000.0,
              "average # of microseconds for sleep operations");
DEFINE_double(sleep_sigma, -1.0,
              "log-normal sigma parameter for sleep duration");
DEFINE_double(burn_avg, 5000.0,
              "average # of microseconds for sleep operations");
DEFINE_double(burn_sigma, -1.0,
              "log-normal sigma parameter for sleep duration");
DEFINE_double(send_size_avg, 16384.0,
              "average # of bytes for send operations");
DEFINE_double(send_size_sigma, -1.0,
              "log-normal sigma parameter for send size");
DEFINE_double(recv_size_avg, 16384.0,
              "average # of bytes for receive operations");
DEFINE_double(recv_size_sigma, -1.0,
              "log-normal sigma parameter for receive size");
DEFINE_double(container_size_avg, 100.0,
              "average # of structs to put in a container");
DEFINE_double(container_size_sigma, -1.0,
              "log-normal sigma parameter for container size");
DEFINE_double(struct_field_size_avg, 64.0,
              "average length of field in BigStructs");
DEFINE_double(struct_field_size_sigma, -1.0,
              "log-normal sigma parameter for struct field size");

namespace apache { namespace thrift { namespace test {

ClientLoadConfig::ClientLoadConfig()
  : WeightedLoadConfig(NUM_OPS) {
  setOpInfo(OP_NOOP, "noop()", FLAGS_weight_noop);
  setOpInfo(OP_ONEWAY_NOOP, "onewayNoop()", FLAGS_weight_oneway_noop);
  setOpInfo(OP_ASYNC_NOOP, "asyncNoop()", FLAGS_weight_async_noop);
  setOpInfo(OP_SLEEP, "sleep()", FLAGS_weight_sleep);
  setOpInfo(OP_ONEWAY_SLEEP, "onewaySleep()", FLAGS_weight_oneway_sleep);
  setOpInfo(OP_BURN, "burn()", FLAGS_weight_burn);
  setOpInfo(OP_ONEWAY_BURN, "onewayBurn()", FLAGS_weight_oneway_burn);
  setOpInfo(OP_BAD_SLEEP, "badSleep()", FLAGS_weight_bad_sleep);
  setOpInfo(OP_BAD_BURN, "badBurn()", FLAGS_weight_bad_burn);
  setOpInfo(OP_THROW_ERROR, "throwError()", FLAGS_weight_throw_error);
  setOpInfo(OP_THROW_UNEXPECTED, "throwUnexpected()",
            FLAGS_weight_throw_unexpected);
  setOpInfo(OP_ONEWAY_THROW, "onewayThrow()", FLAGS_weight_oneway_throw);
  setOpInfo(OP_SEND, "send()", FLAGS_weight_send);
  setOpInfo(OP_ONEWAY_SEND, "onewaySleep()", FLAGS_weight_oneway_send);
  setOpInfo(OP_RECV, "recv()", FLAGS_weight_recv);
  setOpInfo(OP_SENDRECV, "sendrecv()", FLAGS_weight_sendrecv);
  setOpInfo(OP_ECHO, "echo()", FLAGS_weight_echo);
  setOpInfo(OP_ADD, "add()", FLAGS_weight_add);
  setOpInfo(OP_LARGE_CONTAINER, "large_container()",
            FLAGS_weight_large_container);
  setOpInfo(OP_ITER_ALL_FIELDS, "iter_all_fields()",
            FLAGS_weight_iter_all_fields);

  // Look up the hostname, and cache the result so we don't have to perform a
  // resolution for each connection attempt.
  address_.setFromHostPort(FLAGS_server.c_str(), FLAGS_port);
  if (FLAGS_server == "127.0.0.1") {
    char hostname[HOST_NAME_MAX + 1];
    PCHECK(!gethostname(hostname, sizeof(hostname)));
    addressHostname_ = hostname;
  } else {
    struct addrinfo hints, *res, *res0;
    char hostname[NI_MAXHOST] = {};

    memset(&hints, 0, sizeof(hints));
    hints.ai_family = PF_UNSPEC;
    hints.ai_flags = AI_NUMERICHOST;
    PCHECK(
        !getaddrinfo(address_.getAddressStr().c_str(), nullptr, &hints, &res0));

    for (res = res0; res; res = res->ai_next) {
      if (0 ==
          getnameinfo(
              res->ai_addr,
              res->ai_addrlen,
              hostname,
              NI_MAXHOST,
              nullptr,
              0,
              NI_NAMEREQD)) {
        break;
      }
    }
    freeaddrinfo(res0);
    addressHostname_ = hostname;
  }

  // TODO: verify that at least one weight is non-zero
  // TODO: it would be nice to be able to load from a config file
}

uint32_t ClientLoadConfig::pickOpsPerConnection() {
  return FLAGS_ops_per_conn;
}

uint32_t ClientLoadConfig::getAsyncClients() const {
  return FLAGS_async_clients;
}

uint32_t ClientLoadConfig::getAsyncOpsPerClient() const {
  return FLAGS_async_ops;
}

uint32_t ClientLoadConfig::getNumWorkerThreads() const {
  return FLAGS_num_threads;
}

uint64_t ClientLoadConfig::getDesiredQPS() const {
  if (FLAGS_qps <= 0) {
    return 0;
  } else {
    return FLAGS_qps;
  }
}

uint32_t ClientLoadConfig::pickSleepUsec() {
  return pickLogNormal(FLAGS_sleep_avg, FLAGS_sleep_sigma);
}

uint32_t ClientLoadConfig::pickBurnUsec() {
  return pickLogNormal(FLAGS_burn_avg, FLAGS_burn_sigma);
}

uint32_t ClientLoadConfig::pickSendSize() {
  return pickLogNormal(FLAGS_send_size_avg, FLAGS_send_size_sigma);
}

uint32_t ClientLoadConfig::pickRecvSize() {
  return pickLogNormal(FLAGS_recv_size_avg, FLAGS_recv_size_sigma);
}

uint32_t ClientLoadConfig::pickContainerSize() {
  return pickLogNormal(FLAGS_container_size_avg, FLAGS_container_size_sigma);
}

uint32_t ClientLoadConfig::pickStructFieldSize() {
  return pickLogNormal(
    FLAGS_struct_field_size_avg,
    FLAGS_struct_field_size_sigma);
}

bool ClientLoadConfig::useFramedTransport() const {
  return FLAGS_framed;
}

bool ClientLoadConfig::useHeaderProtocol() const {
  return FLAGS_header;
}

bool ClientLoadConfig::useAsync() const {
  return FLAGS_async;
}

bool ClientLoadConfig::useSSL() const {
  return FLAGS_ssl;
}

bool ClientLoadConfig::useSSLTFO() const {
  return FLAGS_use_ssl_tfo;
}

bool ClientLoadConfig::useSingleHost() const {
  return FLAGS_single_host;
}

bool ClientLoadConfig::zlib() const {
  return FLAGS_zlib;
}

std::string ClientLoadConfig::server() const {
  return FLAGS_server;
}

uint32_t ClientLoadConfig::port() const {
  return FLAGS_port;
}

std::string ClientLoadConfig::key() const {
  return FLAGS_key;
}

std::string ClientLoadConfig::cert() const {
  return FLAGS_cert;
}

std::string ClientLoadConfig::trustedCAList() const {
  return FLAGS_trusted_ca_list;
}

std::string ClientLoadConfig::ciphers() const {
  return FLAGS_ciphers;
}

bool ClientLoadConfig::useTickets() const {
  return FLAGS_use_tickets;
}

uint32_t ClientLoadConfig::pickLogNormal(double mean, double sigma) {
  return static_cast<uint32_t>(loadgen::RNG::getLogNormal(mean, sigma));
}

}}} // apache::thrift::test
<|endoftext|>