text:
class DeleteTraceLogForTesting {
 public:
  static void Delete() {
    Singleton<base::debug::TraceLog,
              LeakySingletonTraits<base::debug::TraceLog> >::OnExit(0);
  }
};

// The thread buckets for the sampling profiler.
BASE_EXPORT TRACE_EVENT_API_ATOMIC_WORD g_trace_state[3];

namespace base {
namespace debug {

namespace {

// The overhead of TraceEvent above this threshold will be reported in the
// trace.
const int kOverheadReportThresholdInMicroseconds = 50;

// Controls the number of trace events we will buffer in-memory
// before throwing them away.
const size_t kTraceEventVectorBufferSize = 250000;
const size_t kTraceEventRingBufferSize = kTraceEventVectorBufferSize / 4;
const size_t kTraceEventThreadLocalBufferSize = 64;
const size_t kTraceEventBatchSize = 1000;
const size_t kTraceEventInitialBufferSize = 1024;

const int kThreadFlushTimeoutMs = 1000;

#define MAX_CATEGORY_GROUPS 100

// Parallel arrays g_category_groups and g_category_group_enabled are separate
// so that a pointer to a member of g_category_group_enabled can be easily
// converted to an index into g_category_groups. This allows macros to deal
// only with char enabled pointers from g_category_group_enabled, and we can
// convert internally to determine the category name from the char enabled
// pointer.
const char* g_category_groups[MAX_CATEGORY_GROUPS] = {
  "tracing already shutdown",
  "tracing categories exhausted; must increase MAX_CATEGORY_GROUPS",
  "__metadata",
  // For reporting trace_event overhead. For thread local event buffers only.
  "trace_event_overhead"};

// The enabled flag is char instead of bool so that the API can be used from C.
unsigned char g_category_group_enabled[MAX_CATEGORY_GROUPS] = { 0 };
const int g_category_already_shutdown = 0;
const int g_category_categories_exhausted = 1;
const int g_category_metadata = 2;
const int g_category_trace_event_overhead = 3;
const int g_num_builtin_categories = 4;
int g_category_index = g_num_builtin_categories; // Skip default categories.

// The name of the current thread. This is used to decide if the current
// thread name has changed. We combine all the seen thread names into the
// output name for the thread.
LazyInstance<ThreadLocalPointer<const char> >::Leaky
    g_current_thread_name = LAZY_INSTANCE_INITIALIZER;

const char kRecordUntilFull[] = "record-until-full";
const char kRecordContinuously[] = "record-continuously";
const char kEnableSampling[] = "enable-sampling";

TimeTicks ThreadNow() {
  return TimeTicks::IsThreadNowSupported() ?
      TimeTicks::ThreadNow() : TimeTicks();
}

class TraceBufferRingBuffer : public TraceBuffer {
 public:
  TraceBufferRingBuffer()
      : unused_event_index_(0),
        oldest_event_index_(0) {
    logged_events_.reserve(kTraceEventInitialBufferSize);
  }

  virtual ~TraceBufferRingBuffer() {}

  virtual void AddEvent(const TraceEvent& event) OVERRIDE {
    if (unused_event_index_ < Size())
      logged_events_[unused_event_index_] = event;
    else
      logged_events_.push_back(event);

    unused_event_index_ = NextIndex(unused_event_index_);
    if (unused_event_index_ == oldest_event_index_) {
      oldest_event_index_ = NextIndex(oldest_event_index_);
    }
  }

  virtual bool HasMoreEvents() const OVERRIDE {
    return oldest_event_index_ != unused_event_index_;
  }

  virtual const TraceEvent& NextEvent() OVERRIDE {
    DCHECK(HasMoreEvents());

    size_t next = oldest_event_index_;
    oldest_event_index_ = NextIndex(oldest_event_index_);
    return GetEventAt(next);
  }

  virtual bool IsFull() const OVERRIDE {
    return false;
  }

  virtual size_t CountEnabledByName(
      const unsigned char* category,
      const std::string& event_name) const OVERRIDE {
    size_t notify_count = 0;
    size_t index = oldest_event_index_;
    while (index != unused_event_index_) {
      const TraceEvent& event = GetEventAt(index);
      if (category == event.category_group_enabled() &&
          strcmp(event_name.c_str(), event.name()) == 0) {
        ++notify_count;
      }
      index = NextIndex(index);
    }
    return notify_count;
  }

  virtual const TraceEvent& GetEventAt(size_t index) const OVERRIDE {
    DCHECK(index < logged_events_.size());
    return logged_events_[index];
  }

  virtual size_t Size() const OVERRIDE {
    return logged_events_.size();
  }

  virtual size_t Capacity() const OVERRIDE {
    return kTraceEventRingBufferSize;
  }

 private:
  static size_t NextIndex(size_t index) {
    index++;
    if (index >= kTraceEventRingBufferSize)
      index = 0;
    return index;
  }

  size_t unused_event_index_;
  size_t oldest_event_index_;
  std::vector<TraceEvent> logged_events_;

  DISALLOW_COPY_AND_ASSIGN(TraceBufferRingBuffer);
};

class TraceBufferVector : public TraceBuffer {
 public:
  TraceBufferVector() : current_iteration_index_(0) {
    logged_events_.reserve(kTraceEventInitialBufferSize);
  }

  virtual ~TraceBufferVector() {
  }

  virtual void AddEvent(const TraceEvent& event) OVERRIDE {
    // Note, we have two callers which need to be handled:
    // - AddEventToMainBufferWhileLocked() which has two cases:
    //   - called directly from AddTraceEventWithThreadIdAndTimeStamp()
    //     which checks if buffer is full and does an early exit if full;
    //   - called from ThreadLocalEventBuffer::FlushWhileLocked();
    // - AddThreadNameMetadataEvents().
    // We can not DECHECK(!IsFull()) because we have to add the metadata
    // events and flush thread-local buffers even if the buffer is full.
    logged_events_.push_back(event);
  }

  virtual bool HasMoreEvents() const OVERRIDE {
    return current_iteration_index_ < Size();
  }

  virtual const TraceEvent& NextEvent() OVERRIDE {
    DCHECK(HasMoreEvents());
    return GetEventAt(current_iteration_index_++);
  }

  virtual bool IsFull() const OVERRIDE {
    return Size() >= kTraceEventVectorBufferSize;
  }

  virtual size_t CountEnabledByName(
      const unsigned char* category,
      const std::string& event_name) const OVERRIDE {
    size_t notify_count = 0;
    for (size_t i = 0; i < Size(); i++) {
      const TraceEvent& event = GetEventAt(i);
      if (category == event.category_group_enabled() &&
          strcmp(event_name.c_str(), event.name()) == 0) {
        ++notify_count;
      }
    }
    return notify_count;
  }

  virtual const TraceEvent& GetEventAt(size_t index) const OVERRIDE {
    DCHECK(index < logged_events_.size());
    return logged_events_[index];
  }

  virtual size_t Size() const OVERRIDE {
    return logged_events_.size();
  }

  virtual size_t Capacity() const OVERRIDE {
    return kTraceEventVectorBufferSize;
  }

 private:
  size_t current_iteration_index_;
  std::vector<TraceEvent> logged_events_;

  DISALLOW_COPY_AND_ASSIGN(TraceBufferVector);
};

class TraceBufferDiscardsEvents : public TraceBuffer {
 public:
  virtual ~TraceBufferDiscardsEvents() { }

  virtual void AddEvent(const TraceEvent& event) OVERRIDE {}
  virtual bool HasMoreEvents() const OVERRIDE { return false; }

  virtual const TraceEvent& NextEvent() OVERRIDE {
    NOTREACHED();
    return *static_cast<TraceEvent*>(NULL);
  }

  virtual bool IsFull() const OVERRIDE { return false; }

  virtual size_t CountEnabledByName(
      const unsigned char* category,
      const std::string& event_name) const OVERRIDE {
    return 0;
  }

  virtual size_t Size() const OVERRIDE { return 0; }

  // As this buffer is never full, we can return any positive number.
  virtual size_t Capacity() const OVERRIDE { return 1; }

  virtual const TraceEvent& GetEventAt(size_t index) const OVERRIDE {
    NOTREACHED();
    return *static_cast<TraceEvent*>(NULL);
  }
};

}  // namespace

////////////////////////////////////////////////////////////////////////////////
//
// TraceEvent
//
////////////////////////////////////////////////////////////////////////////////

namespace {

size_t GetAllocLength(const char* str) { return str ? strlen(str) + 1 : 0; }

// Copies |*member| into |*buffer|, sets |*member| to point to this new
// location, and then advances |*buffer| by the amount written.
void CopyTraceEventParameter(char** buffer,
                             const char** member,
                             const char* end) {
  if (*member) {
    size_t written = strlcpy(*buffer, *member, end - *buffer) + 1;
    DCHECK_LE(static_cast<int>(written), end - *buffer);
    *member = *buffer;
    *buffer += written;
  }
}

}  // namespace

TraceEvent::TraceEvent()
    : id_(0u),
      category_group_enabled_(NULL),
      name_(NULL),
      thread_id_(0),
      phase_(TRACE_EVENT_PHASE_BEGIN),
      flags_(0) {
  arg_names_[0] = NULL;
  arg_names_[1] = NULL;
  memset(arg_values_, 0, sizeof(arg_values_));
}

TraceEvent::TraceEvent(
    int thread_id,
    TimeTicks timestamp,
    TimeTicks thread_timestamp,
    char phase,
    const unsigned char* category_group_enabled,
    const char* name,
    unsigned long long id,
    int num_args,
    const char** arg_names,
    const unsigned char* arg_types,
    const unsigned long long* arg_values,
    scoped_ptr<ConvertableToTraceFormat> convertable_values[],
    unsigned char flags)
    : timestamp_(timestamp),
      thread_timestamp_(thread_timestamp),
      id_(id),
      category_group_enabled_(category_group_enabled),
      name_(name),
      thread_id_(thread_id),
      phase_(phase),
      flags_(flags) {

  // Clamp num_args since it may have been set by a third_party library.
  num_args = (num_args > kTraceMaxNumArgs) ? kTraceMaxNumArgs : num_args;
  int i = 0;
  for (; i < num_args; ++i) {
    arg_names_[i] = arg_names[i];
    arg_types_[i] = arg_types[i];

    if (arg_types[i] == TRACE_VALUE_TYPE_CONVERTABLE)
      convertable_values_[i].reset(convertable_values[i].release());
    else
      arg_values_[i].as_uint = arg_values[i];
  }
  for (; i < kTraceMaxNumArgs; ++i) {
    arg_names_[i] = NULL;
    arg_values_[i].as_uint = 0u;
    convertable_values_[i].reset();
    arg_types_[i] = TRACE_VALUE_TYPE_UINT;
  }

  bool copy = !!(flags & TRACE_EVENT_FLAG_COPY);
  size_t alloc_size = 0;
  if (copy) {
    alloc_size += GetAllocLength(name);
    for (i = 0; i < num_args; ++i) {
      alloc_size += GetAllocLength(arg_names_[i]);
      if (arg_types_[i] == TRACE_VALUE_TYPE_STRING)
        arg_types_[i] = TRACE_VALUE_TYPE_COPY_STRING;
    }
  }

  bool arg_is_copy[kTraceMaxNumArgs];
  for (i = 0; i < num_args; ++i) {
    // No copying of convertable types, we retain ownership.
    if (arg_types_[i] == TRACE_VALUE_TYPE_CONVERTABLE)
      continue;

    // We only take a copy of arg_vals if they are of type COPY_STRING.
    arg_is_copy[i] = (arg_types_[i] == TRACE_VALUE_TYPE_COPY_STRING);
    if (arg_is_copy[i])
      alloc_size += GetAllocLength(arg_values_[i].as_string);
  }

  if (alloc_size) {
    parameter_copy_storage_ = new RefCountedString;
    parameter_copy_storage_->data().resize(alloc_size);
    char* ptr = string_as_array(&parameter_copy_storage_->data());
    const char* end = ptr + alloc_size;
    if (copy) {
      CopyTraceEventParameter(&ptr, &name_, end);
      for (i = 0; i < num_args; ++i) {
        CopyTraceEventParameter(&ptr, &arg_names_[i], end);
      }
    }
    for (i = 0; i < num_args; ++i) {
      if (arg_types_[i] == TRACE_VALUE_TYPE_CONVERTABLE)
        continue;
      if (arg_is_copy[i])
        CopyTraceEventParameter(&ptr, &arg_values_[i].as_string, end);
    }
    DCHECK_EQ(end, ptr) << "Overrun by " << ptr - end;
  }
}

TraceEvent::TraceEvent(const TraceEvent& other)
    : timestamp_(other.timestamp_),
      thread_timestamp_(other.thread_timestamp_),
      id_(other.id_),
      category_group_enabled_(other.category_group_enabled_),
      name_(other.name_),
      thread_id_(other.thread_id_),
      phase_(other.phase_),
      flags_(other.flags_) {
  parameter_copy_storage_ = other.parameter_copy_storage_;

  for (int i = 0; i < kTraceMaxNumArgs; ++i) {
    arg_values_[i] = other.arg_values_[i];
    arg_names_[i] = other.arg_names_[i];
    arg_types_[i] = other.arg_types_[i];

    if (arg_types_[i] == TRACE_VALUE_TYPE_CONVERTABLE) {
      convertable_values_[i].reset(
          const_cast<TraceEvent*>(&other)->convertable_values_[i].release());
    } else {
      convertable_values_[i].reset();
    }
  }
}

TraceEvent& TraceEvent::operator=(const TraceEvent& other) {
  if (this == &other)
    return *this;

  timestamp_ = other.timestamp_;
  thread_timestamp_ = other.thread_timestamp_;
  id_ = other.id_;
  category_group_enabled_ = other.category_group_enabled_;
  name_ = other.name_;
  parameter_copy_storage_ = other.parameter_copy_storage_;
  thread_id_ = other.thread_id_;
  phase_ = other.phase_;
  flags_ = other.flags_;

  for (int i = 0; i < kTraceMaxNumArgs; ++i) {
    arg_values_[i] = other.arg_values_[i];
    arg_names_[i] = other.arg_names_[i];
    arg_types_[i] = other.arg_types_[i];

    if (arg_types_[i] == TRACE_VALUE_TYPE_CONVERTABLE) {
      convertable_values_[i].reset(
          const_cast<TraceEvent*>(&other)->convertable_values_[i].release());
    } else {
      convertable_values_[i].reset();
    }
  }
  return *this;
}

TraceEvent::~TraceEvent() {
}

// static
void TraceEvent::AppendValueAsJSON(unsigned char type,
                                   TraceEvent::TraceValue value,
                                   std::string* out) {
  std::string::size_type start_pos;
  switch (type) {
    case TRACE_VALUE_TYPE_BOOL:
      *out += value.as_bool ? "true" : "false";
      break;
    case TRACE_VALUE_TYPE_UINT:
      StringAppendF(out, "%" PRIu64, static_cast<uint64>(value.as_uint));
      break;
    case TRACE_VALUE_TYPE_INT:
      StringAppendF(out, "%" PRId64, static_cast<int64>(value.as_int));
      break;
    case TRACE_VALUE_TYPE_DOUBLE:
      StringAppendF(out, "%f", value.as_double);
      break;
    case TRACE_VALUE_TYPE_POINTER:
      // JSON only supports double and int numbers.
      // So as not to lose bits from a 64-bit pointer, output as a hex string.
      StringAppendF(out, "\"0x%" PRIx64 "\"", static_cast<uint64>(
                                     reinterpret_cast<intptr_t>(
                                     value.as_pointer)));
      break;
    case TRACE_VALUE_TYPE_STRING:
    case TRACE_VALUE_TYPE_COPY_STRING:
      *out += "\"";
      start_pos = out->size();
      *out += value.as_string ? value.as_string : "NULL";
      // insert backslash before special characters for proper json format.
      while ((start_pos = out->find_first_of("\\\"", start_pos)) !=
             std::string::npos) {
        out->insert(start_pos, 1, '\\');
        // skip inserted escape character and following character.
        start_pos += 2;
      }
      *out += "\"";
      break;
    default:
      NOTREACHED() << "Don't know how to print this value";
      break;
  }
}

void TraceEvent::AppendAsJSON(std::string* out) const {
  int64 time_int64 = timestamp_.ToInternalValue();
  int process_id = TraceLog::GetInstance()->process_id();
  // Category group checked at category creation time.
  DCHECK(!strchr(name_, '"'));
  StringAppendF(out,
      "{\"cat\":\"%s\",\"pid\":%i,\"tid\":%i,\"ts\":%" PRId64 ","
      "\"ph\":\"%c\",\"name\":\"%s\",\"args\":{",
      TraceLog::GetCategoryGroupName(category_group_enabled_),
      process_id,
      thread_id_,
      time_int64,
      phase_,
      name_);

  // Output argument names and values, stop at first NULL argument name.
  for (int i = 0; i < kTraceMaxNumArgs && arg_names_[i]; ++i) {
    if (i > 0)
      *out += ",";
    *out += "\"";
    *out += arg_names_[i];
    *out += "\":";

    if (arg_types_[i] == TRACE_VALUE_TYPE_CONVERTABLE)
      convertable_values_[i]->AppendAsTraceFormat(out);
    else
      AppendValueAsJSON(arg_types_[i], arg_values_[i], out);
  }
  *out += "}";

  // Output tts if thread_timestamp is valid.
  if (!thread_timestamp_.is_null()) {
    int64 thread_time_int64 = thread_timestamp_.ToInternalValue();
    StringAppendF(out, ",\"tts\":%" PRId64, thread_time_int64);
  }

  // If id_ is set, print it out as a hex string so we don't loose any
  // bits (it might be a 64-bit pointer).
  if (flags_ & TRACE_EVENT_FLAG_HAS_ID)
    StringAppendF(out, ",\"id\":\"0x%" PRIx64 "\"", static_cast<uint64>(id_));

  // Instant events also output their scope.
  if (phase_ == TRACE_EVENT_PHASE_INSTANT) {
    char scope = '?';
    switch (flags_ & TRACE_EVENT_FLAG_SCOPE_MASK) {
      case TRACE_EVENT_SCOPE_GLOBAL:
        scope = TRACE_EVENT_SCOPE_NAME_GLOBAL;
        break;

      case TRACE_EVENT_SCOPE_PROCESS:
        scope = TRACE_EVENT_SCOPE_NAME_PROCESS;
        break;

      case TRACE_EVENT_SCOPE_THREAD:
        scope = TRACE_EVENT_SCOPE_NAME_THREAD;
        break;
    }
    StringAppendF(out, ",\"s\":\"%c\"", scope);
  }

  *out += "}";
}

void TraceEvent::AppendPrettyPrinted(std::ostringstream* out) const {
  *out << name_ << "[";
  *out << TraceLog::GetCategoryGroupName(category_group_enabled_);
  *out << "]";
  if (arg_names_[0]) {
    *out << ", {";
    for (int i = 0; i < kTraceMaxNumArgs && arg_names_[i]; ++i) {
      if (i > 0)
        *out << ", ";
      *out << arg_names_[i] << ":";
      std::string value_as_text;

      if (arg_types_[i] == TRACE_VALUE_TYPE_CONVERTABLE)
        convertable_values_[i]->AppendAsTraceFormat(&value_as_text);
      else
        AppendValueAsJSON(arg_types_[i], arg_values_[i], &value_as_text);

      *out << value_as_text;
    }
    *out << "}";
  }
}

////////////////////////////////////////////////////////////////////////////////
//
// TraceResultBuffer
//
////////////////////////////////////////////////////////////////////////////////

TraceResultBuffer::OutputCallback
    TraceResultBuffer::SimpleOutput::GetCallback() {
  return Bind(&SimpleOutput::Append, Unretained(this));
}

void TraceResultBuffer::SimpleOutput::Append(
    const std::string& json_trace_output) {
  json_output += json_trace_output;
}

TraceResultBuffer::TraceResultBuffer() : append_comma_(false) {
}

TraceResultBuffer::~TraceResultBuffer() {
}

void TraceResultBuffer::SetOutputCallback(
    const OutputCallback& json_chunk_callback) {
  output_callback_ = json_chunk_callback;
}

void TraceResultBuffer::Start() {
  append_comma_ = false;
  output_callback_.Run("[");
}

void TraceResultBuffer::AddFragment(const std::string& trace_fragment) {
  if (append_comma_)
    output_callback_.Run(",");
  append_comma_ = true;
  output_callback_.Run(trace_fragment);
}

void TraceResultBuffer::Finish() {
  output_callback_.Run("]");
}

////////////////////////////////////////////////////////////////////////////////
//
// TraceSamplingThread
//
////////////////////////////////////////////////////////////////////////////////
class TraceBucketData;
typedef base::Callback<void(TraceBucketData*)> TraceSampleCallback;

class TraceBucketData {
 public:
  TraceBucketData(base::subtle::AtomicWord* bucket,
                  const char* name,
                  TraceSampleCallback callback);
  ~TraceBucketData();

  TRACE_EVENT_API_ATOMIC_WORD* bucket;
  const char* bucket_name;
  TraceSampleCallback callback;
};

// This object must be created on the IO thread.
class TraceSamplingThread : public PlatformThread::Delegate {
 public:
  TraceSamplingThread();
  virtual ~TraceSamplingThread();

  // Implementation of PlatformThread::Delegate:
  virtual void ThreadMain() OVERRIDE;

  static void DefaultSampleCallback(TraceBucketData* bucekt_data);

  void Stop();
  void InstallWaitableEventForSamplingTesting(WaitableEvent* waitable_event);

 private:
  friend class TraceLog;

  void GetSamples();
  // Not thread-safe. Once the ThreadMain has been called, this can no longer
  // be called.
  void RegisterSampleBucket(TRACE_EVENT_API_ATOMIC_WORD* bucket,
                            const char* const name,
                            TraceSampleCallback callback);
  // Splits a combined "category\0name" into the two component parts.
  static void ExtractCategoryAndName(const char* combined,
                                     const char** category,
                                     const char** name);
  std::vector<TraceBucketData> sample_buckets_;
  bool thread_running_;
  scoped_ptr<CancellationFlag> cancellation_flag_;
  scoped_ptr<WaitableEvent> waitable_event_for_testing_;
};

TraceSamplingThread::TraceSamplingThread()
    : thread_running_(false) {
  cancellation_flag_.reset(new CancellationFlag);
}

TraceSamplingThread::~TraceSamplingThread() {
}

void TraceSamplingThread::ThreadMain() {
  PlatformThread::SetName("Sampling Thread");
  thread_running_ = true;
  const int kSamplingFrequencyMicroseconds = 1000;
  while (!cancellation_flag_->IsSet()) {
    PlatformThread::Sleep(
        TimeDelta::FromMicroseconds(kSamplingFrequencyMicroseconds));
    GetSamples();
    if (waitable_event_for_testing_.get())
      waitable_event_for_testing_->Signal();
  }
}

// static
void TraceSamplingThread::DefaultSampleCallback(TraceBucketData* bucket_data) {
  TRACE_EVENT_API_ATOMIC_WORD category_and_name =
      TRACE_EVENT_API_ATOMIC_LOAD(*bucket_data->bucket);
  if (!category_and_name)
    return;
  const char* const combined =
      reinterpret_cast<const char* const>(category_and_name);
  const char* category_group;
  const char* name;
  ExtractCategoryAndName(combined, &category_group, &name);
  TRACE_EVENT_API_ADD_TRACE_EVENT(TRACE_EVENT_PHASE_SAMPLE,
      TraceLog::GetCategoryGroupEnabled(category_group),
      name, 0, 0, NULL, NULL, NULL, NULL, 0);
}

void TraceSamplingThread::GetSamples() {
  for (size_t i = 0; i < sample_buckets_.size(); ++i) {
    TraceBucketData* bucket_data = &sample_buckets_[i];
    bucket_data->callback.Run(bucket_data);
  }
}

void TraceSamplingThread::RegisterSampleBucket(
    TRACE_EVENT_API_ATOMIC_WORD* bucket,
    const char* const name,
    TraceSampleCallback callback) {
  DCHECK(!thread_running_);
  sample_buckets_.push_back(TraceBucketData(bucket, name, callback));
}

// static
void TraceSamplingThread::ExtractCategoryAndName(const char* combined,
                                                 const char** category,
                                                 const char** name) {
  *category = combined;
  *name = &combined[strlen(combined) + 1];
}

void TraceSamplingThread::Stop() {
  cancellation_flag_->Set();
}

void TraceSamplingThread::InstallWaitableEventForSamplingTesting(
    WaitableEvent* waitable_event) {
  waitable_event_for_testing_.reset(waitable_event);
}

TraceBucketData::TraceBucketData(base::subtle::AtomicWord* bucket,
                                 const char* name,
                                 TraceSampleCallback callback)
    : bucket(bucket),
      bucket_name(name),
      callback(callback) {
}

TraceBucketData::~TraceBucketData() {
}

////////////////////////////////////////////////////////////////////////////////
//
// TraceLog
//
////////////////////////////////////////////////////////////////////////////////

class TraceLog::ThreadLocalEventBuffer
    : public MessageLoop::DestructionObserver {
 public:
  ThreadLocalEventBuffer(TraceLog* trace_log);
  virtual ~ThreadLocalEventBuffer();

  void AddEvent(const TraceEvent& event, NotificationHelper* notifier);
  void ReportOverhead(const TimeTicks& event_timestamp,
                      const TimeTicks& event_thread_timestamp);

 private:
  // MessageLoop::DestructionObserver
  virtual void WillDestroyCurrentMessageLoop() OVERRIDE;

  void FlushWhileLocked(NotificationHelper* notifier);

  void CheckThisIsCurrentBuffer() {
    DCHECK(trace_log_->thread_local_event_buffer_.Get() == this);
  }

  // Since TraceLog is a leaky singleton, trace_log_ will always be valid
  // as long as the thread exists.
  TraceLog* trace_log_;
  std::vector<TraceEvent> logged_events_;
  int event_count_;
  TimeDelta overhead_;

  DISALLOW_COPY_AND_ASSIGN(ThreadLocalEventBuffer);
};

TraceLog::ThreadLocalEventBuffer::ThreadLocalEventBuffer(TraceLog* trace_log)
    : trace_log_(trace_log),
      event_count_(0) {
  logged_events_.reserve(kTraceEventThreadLocalBufferSize);

  // ThreadLocalEventBuffer is created only if the thread has a message loop, so
  // the following message_loop won't be NULL.
  MessageLoop* message_loop = MessageLoop::current();
  message_loop->AddDestructionObserver(this);

  AutoLock lock(trace_log->lock_);
  trace_log->thread_message_loops_.insert(message_loop);
}

TraceLog::ThreadLocalEventBuffer::~ThreadLocalEventBuffer() {
  CheckThisIsCurrentBuffer();
  MessageLoop::current()->RemoveDestructionObserver(this);

  // Zero event_count_ happens in either of the following cases:
  // - no event generated for the thread;
  // - the thread has no message loop;
  // - trace_event_overhead is disabled.
  if (event_count_) {
    const char* arg_names[2] = { "event_count", "average_overhead" };
    unsigned char arg_types[2];
    unsigned long long arg_values[2];
    trace_event_internal::SetTraceValue(
        event_count_, &arg_types[0], &arg_values[0]);
    trace_event_internal::SetTraceValue(
        overhead_.InMillisecondsF() / event_count_,
        &arg_types[1], &arg_values[1]);
    logged_events_.push_back(TraceEvent(
        static_cast<int>(PlatformThread::CurrentId()),
        TimeTicks(), TimeTicks(), TRACE_EVENT_PHASE_METADATA,
        &g_category_group_enabled[g_category_metadata],
        "trace_event_overhead", trace_event_internal::kNoEventId,
        2, arg_names, arg_types, arg_values, NULL,
        TRACE_EVENT_FLAG_NONE));
  }

  NotificationHelper notifier(trace_log_);
  {
    AutoLock lock(trace_log_->lock_);
    FlushWhileLocked(&notifier);
    trace_log_->thread_message_loops_.erase(MessageLoop::current());
  }
  trace_log_->thread_local_event_buffer_.Set(NULL);
  notifier.SendNotificationIfAny();
}

void TraceLog::ThreadLocalEventBuffer::AddEvent(const TraceEvent& event,
                                                NotificationHelper* notifier) {
  CheckThisIsCurrentBuffer();
  logged_events_.push_back(event);
  if (logged_events_.size() >= kTraceEventThreadLocalBufferSize) {
    AutoLock lock(trace_log_->lock_);
    FlushWhileLocked(notifier);
  }
}

void TraceLog::ThreadLocalEventBuffer::ReportOverhead(
    const TimeTicks& event_timestamp, const TimeTicks& event_thread_timestamp) {
  if (!g_category_group_enabled[g_category_trace_event_overhead])
    return;

  event_count_++;
  TimeTicks now =
      TimeTicks::NowFromSystemTraceTime() - trace_log_->time_offset_;
  TimeDelta overhead = now - event_timestamp;
  if (overhead.InMicroseconds() >= kOverheadReportThresholdInMicroseconds) {
    int thread_id = static_cast<int>(PlatformThread::CurrentId());
    // TODO(wangxianzhu): Use X event when it's ready.
    logged_events_.push_back(TraceEvent(
        thread_id, event_timestamp, event_thread_timestamp,
        TRACE_EVENT_PHASE_BEGIN,
        &g_category_group_enabled[g_category_trace_event_overhead],
        "overhead",
        0, 0, NULL, NULL, NULL, NULL, 0));
    logged_events_.push_back(TraceEvent(
        thread_id, now, ThreadNow(),
        TRACE_EVENT_PHASE_END,
        &g_category_group_enabled[g_category_trace_event_overhead],
        "overhead",
        0, 0, NULL, NULL, NULL, NULL, 0));
  }
  overhead_ += overhead;
}

void TraceLog::ThreadLocalEventBuffer::WillDestroyCurrentMessageLoop() {
  delete this;
}

void TraceLog::ThreadLocalEventBuffer::FlushWhileLocked(
    NotificationHelper* notifier) {
  trace_log_->lock_.AssertAcquired();
  for (size_t i = 0; i < logged_events_.size(); ++i) {
    trace_log_->AddEventToMainBufferWhileLocked(logged_events_[i]);
  }
  logged_events_.resize(0);
  trace_log_->CheckIfBufferIsFullWhileLocked(notifier);
}

TraceLog::NotificationHelper::NotificationHelper(TraceLog* trace_log)
    : trace_log_(trace_log),
      notification_(0) {
}

TraceLog::NotificationHelper::~NotificationHelper() {
}

void TraceLog::NotificationHelper::AddNotificationWhileLocked(
    int notification) {
  trace_log_->lock_.AssertAcquired();
  if (trace_log_->notification_callback_.is_null())
    return;
  if (notification_ == 0)
    callback_copy_ = trace_log_->notification_callback_;
  notification_ |= notification;
}

void TraceLog::NotificationHelper::SendNotificationIfAny() {
  if (notification_)
    callback_copy_.Run(notification_);
}

// static
TraceLog* TraceLog::GetInstance() {
  return Singleton<TraceLog, LeakySingletonTraits<TraceLog> >::get();
}

// static
// Note, if you add more options here you also need to update:
// content/browser/devtools/devtools_tracing_handler:TraceOptionsFromString
TraceLog::Options TraceLog::TraceOptionsFromString(const std::string& options) {
  std::vector<std::string> split;
  base::SplitString(options, ',', &split);
  int ret = 0;
  for (std::vector<std::string>::iterator iter = split.begin();
       iter != split.end();
       ++iter) {
    if (*iter == kRecordUntilFull) {
      ret |= RECORD_UNTIL_FULL;
    } else if (*iter == kRecordContinuously) {
      ret |= RECORD_CONTINUOUSLY;
    } else if (*iter == kEnableSampling) {
      ret |= ENABLE_SAMPLING;
    } else {
      NOTREACHED();  // Unknown option provided.
    }
  }
  if (!(ret & RECORD_UNTIL_FULL) && !(ret & RECORD_CONTINUOUSLY))
    ret |= RECORD_UNTIL_FULL;  // Default when no options are specified.

  return static_cast<Options>(ret);
}

TraceLog::TraceLog()
    : enable_count_(0),
      num_traces_recorded_(0),
      buffer_is_full_(0),
      event_callback_(0),
      dispatching_to_observer_list_(false),
      process_sort_index_(0),
      process_id_hash_(0),
      process_id_(0),
      watch_category_(0),
      trace_options_(RECORD_UNTIL_FULL),
      sampling_thread_handle_(0),
      category_filter_(CategoryFilter::kDefaultCategoryFilterString),
      flush_count_(0) {
  // Trace is enabled or disabled on one thread while other threads are
  // accessing the enabled flag. We don't care whether edge-case events are
  // traced or not, so we allow races on the enabled flag to keep the trace
  // macros fast.
  // TODO(jbates): ANNOTATE_BENIGN_RACE_SIZED crashes windows TSAN bots:
  // ANNOTATE_BENIGN_RACE_SIZED(g_category_group_enabled,
  //                            sizeof(g_category_group_enabled),
  //                           "trace_event category enabled");
  for (int i = 0; i < MAX_CATEGORY_GROUPS; ++i) {
    ANNOTATE_BENIGN_RACE(&g_category_group_enabled[i],
                         "trace_event category enabled");
  }
#if defined(OS_NACL)  // NaCl shouldn't expose the process id.
  SetProcessID(0);
#else
  SetProcessID(static_cast<int>(GetCurrentProcId()));

  // NaCl also shouldn't access the command line.
  if (CommandLine::InitializedForCurrentProcess() &&
      CommandLine::ForCurrentProcess()->HasSwitch(switches::kTraceToConsole)) {
    std::string category_string =
        CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
            switches::kTraceToConsole);

    if (category_string.empty())
      category_string = "*";

    SetEnabled(CategoryFilter(category_string), ECHO_TO_CONSOLE);
  }
#endif

  logged_events_.reset(GetTraceBuffer());
}

TraceLog::~TraceLog() {
}

const unsigned char* TraceLog::GetCategoryGroupEnabled(
    const char* category_group) {
  TraceLog* tracelog = GetInstance();
  if (!tracelog) {
    DCHECK(!g_category_group_enabled[g_category_already_shutdown]);
    return &g_category_group_enabled[g_category_already_shutdown];
  }
  return tracelog->GetCategoryGroupEnabledInternal(category_group);
}

const char* TraceLog::GetCategoryGroupName(
    const unsigned char* category_group_enabled) {
  // Calculate the index of the category group by finding
  // category_group_enabled in g_category_group_enabled array.
  uintptr_t category_begin =
      reinterpret_cast<uintptr_t>(g_category_group_enabled);
  uintptr_t category_ptr = reinterpret_cast<uintptr_t>(category_group_enabled);
  DCHECK(category_ptr >= category_begin &&
         category_ptr < reinterpret_cast<uintptr_t>(
             g_category_group_enabled + MAX_CATEGORY_GROUPS)) <<
      "out of bounds category pointer";
  uintptr_t category_index =
      (category_ptr - category_begin) / sizeof(g_category_group_enabled[0]);
  return g_category_groups[category_index];
}

void TraceLog::UpdateCategoryGroupEnabledFlag(int category_index) {
  bool is_enabled = enable_count_ && category_filter_.IsCategoryGroupEnabled(
      g_category_groups[category_index]);
  SetCategoryGroupEnabled(category_index, is_enabled);
}

void TraceLog::UpdateCategoryGroupEnabledFlags() {
  for (int i = 0; i < g_category_index; i++)
    UpdateCategoryGroupEnabledFlag(i);
}

void TraceLog::SetCategoryGroupEnabled(int category_index, bool is_enabled) {
  g_category_group_enabled[category_index] =
      is_enabled ? CATEGORY_GROUP_ENABLED : 0;

#if defined(OS_ANDROID)
  ApplyATraceEnabledFlag(&g_category_group_enabled[category_index]);
#endif
}

bool TraceLog::IsCategoryGroupEnabled(
    const unsigned char* category_group_enabled) {
  // On Android, ATrace and normal trace can be enabled independently.
  // This function checks if the normal trace is enabled.
  return *category_group_enabled & CATEGORY_GROUP_ENABLED;
}

const unsigned char* TraceLog::GetCategoryGroupEnabledInternal(
    const char* category_group) {
  DCHECK(!strchr(category_group, '"')) <<
      "Category groups may not contain double quote";
  AutoLock lock(lock_);

  unsigned char* category_group_enabled = NULL;
  // Search for pre-existing category group.
  for (int i = 0; i < g_category_index; i++) {
    if (strcmp(g_category_groups[i], category_group) == 0) {
      category_group_enabled = &g_category_group_enabled[i];
      break;
    }
  }

  if (!category_group_enabled) {
    // Create a new category group
    DCHECK(g_category_index < MAX_CATEGORY_GROUPS) <<
        "must increase MAX_CATEGORY_GROUPS";
    if (g_category_index < MAX_CATEGORY_GROUPS) {
      int new_index = g_category_index++;
      // Don't hold on to the category_group pointer, so that we can create
      // category groups with strings not known at compile time (this is
      // required by SetWatchEvent).
      const char* new_group = strdup(category_group);
      ANNOTATE_LEAKING_OBJECT_PTR(new_group);
      g_category_groups[new_index] = new_group;
      DCHECK(!g_category_group_enabled[new_index]);
      // Note that if both included and excluded patterns in the
      // CategoryFilter are empty, we exclude nothing,
      // thereby enabling this category group.
      UpdateCategoryGroupEnabledFlag(new_index);
      category_group_enabled = &g_category_group_enabled[new_index];
    } else {
      category_group_enabled =
          &g_category_group_enabled[g_category_categories_exhausted];
    }
  }
  return category_group_enabled;
}

void TraceLog::GetKnownCategoryGroups(
    std::vector<std::string>* category_groups) {
  AutoLock lock(lock_);
  category_groups->push_back(
      g_category_groups[g_category_trace_event_overhead]);
  for (int i = g_num_builtin_categories; i < g_category_index; i++)
    category_groups->push_back(g_category_groups[i]);
}

void TraceLog::SetEnabled(const CategoryFilter& category_filter,
                          Options options) {
  std::vector<EnabledStateObserver*> observer_list;
  {
    AutoLock lock(lock_);

    // Can't enable tracing when Flush() is in progress.
    DCHECK(!flush_message_loop_proxy_.get());

    Options old_options = trace_options();

    if (enable_count_++ > 0) {
      if (options != old_options) {
        DLOG(ERROR) << "Attemting to re-enable tracing with a different "
                    << "set of options.";
      }

      category_filter_.Merge(category_filter);
      UpdateCategoryGroupEnabledFlags();
      return;
    }

    if (options != old_options) {
      subtle::NoBarrier_Store(&trace_options_, options);
      logged_events_.reset(GetTraceBuffer());
      subtle::NoBarrier_Store(&buffer_is_full_, 0);
    }

    if (dispatching_to_observer_list_) {
      DLOG(ERROR) <<
          "Cannot manipulate TraceLog::Enabled state from an observer.";
      return;
    }

    num_traces_recorded_++;

    category_filter_ = CategoryFilter(category_filter);
    UpdateCategoryGroupEnabledFlags();

    if (options & ENABLE_SAMPLING) {
      sampling_thread_.reset(new TraceSamplingThread);
      sampling_thread_->RegisterSampleBucket(
          &g_trace_state[0],
          "bucket0",
          Bind(&TraceSamplingThread::DefaultSampleCallback));
      sampling_thread_->RegisterSampleBucket(
          &g_trace_state[1],
          "bucket1",
          Bind(&TraceSamplingThread::DefaultSampleCallback));
      sampling_thread_->RegisterSampleBucket(
          &g_trace_state[2],
          "bucket2",
          Bind(&TraceSamplingThread::DefaultSampleCallback));
      if (!PlatformThread::Create(
            0, sampling_thread_.get(), &sampling_thread_handle_)) {
        DCHECK(false) << "failed to create thread";
      }
    }

    dispatching_to_observer_list_ = true;
    observer_list = enabled_state_observer_list_;
  }
  // Notify observers outside the lock in case they trigger trace events.
  for (size_t i = 0; i < observer_list.size(); ++i)
    observer_list[i]->OnTraceLogEnabled();

  {
    AutoLock lock(lock_);
    dispatching_to_observer_list_ = false;
  }
}

const CategoryFilter& TraceLog::GetCurrentCategoryFilter() {
  AutoLock lock(lock_);
  DCHECK(enable_count_ > 0);
  return category_filter_;
}

void TraceLog::SetDisabled() {
  std::vector<EnabledStateObserver*> observer_list;
  {
    AutoLock lock(lock_);
    DCHECK(enable_count_ > 0);

    if (--enable_count_ != 0)
      return;

    if (dispatching_to_observer_list_) {
      DLOG(ERROR)
          << "Cannot manipulate TraceLog::Enabled state from an observer.";
      return;
    }

    if (sampling_thread_.get()) {
      // Stop the sampling thread.
      sampling_thread_->Stop();
      lock_.Release();
      PlatformThread::Join(sampling_thread_handle_);
      lock_.Acquire();
      sampling_thread_handle_ = PlatformThreadHandle();
      sampling_thread_.reset();
    }

    category_filter_.Clear();
    subtle::NoBarrier_Store(&watch_category_, 0);
    watch_event_name_ = "";
    UpdateCategoryGroupEnabledFlags();
    AddMetadataEvents();

    dispatching_to_observer_list_ = true;
    observer_list = enabled_state_observer_list_;
  }

  // Dispatch to observers outside the lock in case the observer triggers a
  // trace event.
  for (size_t i = 0; i < observer_list.size(); ++i)
    observer_list[i]->OnTraceLogDisabled();

  {
    AutoLock lock(lock_);
    dispatching_to_observer_list_ = false;
  }
}

int TraceLog::GetNumTracesRecorded() {
  AutoLock lock(lock_);
  if (enable_count_ == 0)
    return -1;
  return num_traces_recorded_;
}

void TraceLog::AddEnabledStateObserver(EnabledStateObserver* listener) {
  enabled_state_observer_list_.push_back(listener);
}

void TraceLog::RemoveEnabledStateObserver(EnabledStateObserver* listener) {
  std::vector<EnabledStateObserver*>::iterator it =
      std::find(enabled_state_observer_list_.begin(),
                enabled_state_observer_list_.end(),
                listener);
  if (it != enabled_state_observer_list_.end())
    enabled_state_observer_list_.erase(it);
}

bool TraceLog::HasEnabledStateObserver(EnabledStateObserver* listener) const {
  std::vector<EnabledStateObserver*>::const_iterator it =
      std::find(enabled_state_observer_list_.begin(),
                enabled_state_observer_list_.end(),
                listener);
  return it != enabled_state_observer_list_.end();
}

float TraceLog::GetBufferPercentFull() const {
  return static_cast<float>(static_cast<double>(logged_events_->Size()) /
                            logged_events_->Capacity());
}

void TraceLog::SetNotificationCallback(
    const TraceLog::NotificationCallback& cb) {
  AutoLock lock(lock_);
  notification_callback_ = cb;
}

TraceBuffer* TraceLog::GetTraceBuffer() {
  Options options = trace_options();
  if (options & RECORD_CONTINUOUSLY)
    return new TraceBufferRingBuffer();
  else if (options & ECHO_TO_CONSOLE)
    return new TraceBufferDiscardsEvents();
  return new TraceBufferVector();
}

void TraceLog::AddEventToMainBufferWhileLocked(const TraceEvent& trace_event) {
  // Don't check buffer_is_full_ because we want the remaining thread-local
  // events to be flushed into the main buffer with this method, otherwise
  // we may lose some early events of a thread that generates events sparsely.
  lock_.AssertAcquired();
  logged_events_->AddEvent(trace_event);
}

void TraceLog::CheckIfBufferIsFullWhileLocked(NotificationHelper* notifier) {
  lock_.AssertAcquired();
  if (!subtle::NoBarrier_Load(&buffer_is_full_) && logged_events_->IsFull()) {
    subtle::NoBarrier_Store(&buffer_is_full_,
                            static_cast<subtle::AtomicWord>(1));
    notifier->AddNotificationWhileLocked(TRACE_BUFFER_FULL);
  }
}

void TraceLog::SetEventCallback(EventCallback cb) {
  subtle::NoBarrier_Store(&event_callback_,
                          reinterpret_cast<subtle::AtomicWord>(cb));
};

// Flush() works as the following:
// 1. Flush() is called in threadA whose message loop is saved in
//    flush_message_loop_proxy_;
// 2. If thread_message_loops_ is not empty, threadA posts task to each message
//    loop to flush the thread local buffers; otherwise finish the flush;
// 3. FlushCurrentThread() deletes the thread local event buffer:
//    - The last batch of events of the thread are flushed into the main buffer;
//    - The message loop will be removed from thread_message_loops_;
//    If this is the last message loop, finish the flush;
// 4. If any thread hasn't finish its flush in time, finish the flush.
void TraceLog::Flush(const TraceLog::OutputCallback& cb) {
  if (IsEnabled()) {
    // Can't flush when tracing is enabled because otherwise PostTask would
    // - it generates more trace events;
    // - it deschedules the calling thread on some platforms causing inaccurate
    //   timing of the trace events.
    scoped_refptr<RefCountedString> empty_result = new RefCountedString;
    if (!cb.is_null())
      cb.Run(empty_result, false);
    LOG(WARNING) << "Ignored TraceLog::Flush called when tracing is enabled";
    return;
  }

  int flush_count;
  {
    AutoLock lock(lock_);
    flush_count = ++flush_count_;
    DCHECK(!flush_message_loop_proxy_.get());
    flush_message_loop_proxy_ = MessageLoopProxy::current();
    DCHECK(!thread_message_loops_.size() || flush_message_loop_proxy_.get());
    flush_output_callback_ = cb;

    if (thread_message_loops_.size()) {
      for (hash_set<MessageLoop*>::const_iterator it =
           thread_message_loops_.begin();
           it != thread_message_loops_.end(); ++it) {
        (*it)->PostTask(
            FROM_HERE,
            Bind(&TraceLog::FlushCurrentThread, Unretained(this), flush_count));
      }
      flush_message_loop_proxy_->PostDelayedTask(
          FROM_HERE,
          Bind(&TraceLog::OnFlushTimeout, Unretained(this), flush_count),
          TimeDelta::FromMilliseconds(kThreadFlushTimeoutMs));
      return;
    }
  }

  FinishFlush(flush_count);
}

void TraceLog::FinishFlush(int flush_count) {
  scoped_ptr<TraceBuffer> previous_logged_events;
  OutputCallback flush_output_callback;

  {
    AutoLock lock(lock_);
    if (flush_count != flush_count_)
      return;

    previous_logged_events.swap(logged_events_);
    logged_events_.reset(GetTraceBuffer());
    subtle::NoBarrier_Store(&buffer_is_full_, 0);
    flush_message_loop_proxy_ = NULL;
    flush_output_callback = flush_output_callback_;
    flush_output_callback_.Reset();
  }

  if (flush_output_callback.is_null())
    return;

  bool has_more_events = previous_logged_events->HasMoreEvents();
  // The callback need to be called at least once even if there is no events
  // to let the caller know the completion of flush.
  do {
    scoped_refptr<RefCountedString> json_events_str_ptr =
        new RefCountedString();

    for (size_t i = 0; has_more_events && i < kTraceEventBatchSize; ++i) {
      if (i > 0)
        *(&(json_events_str_ptr->data())) += ",";

      previous_logged_events->NextEvent().AppendAsJSON(
          &(json_events_str_ptr->data()));

      has_more_events = previous_logged_events->HasMoreEvents();
    }

    flush_output_callback.Run(json_events_str_ptr, has_more_events);
  } while (has_more_events);
}

// Run in each thread holding a local event buffer.
void TraceLog::FlushCurrentThread(int flush_count) {
  {
    AutoLock lock(lock_);
    if (flush_count != flush_count_ || !flush_message_loop_proxy_) {
      // This is late. The corresponding flush has finished.
      return;
    }
  }

  delete thread_local_event_buffer_.Get();

  {
    AutoLock lock(lock_);
    if (flush_count != flush_count_ || !flush_message_loop_proxy_ ||
        thread_message_loops_.size())
      return;

    flush_message_loop_proxy_->PostTask(
        FROM_HERE,
        Bind(&TraceLog::FinishFlush, Unretained(this), flush_count));
  }
}

void TraceLog::OnFlushTimeout(int flush_count) {
  {
    AutoLock lock(lock_);
    if (flush_count != flush_count_ || !flush_message_loop_proxy_) {
      // Flush has finished before timeout.
      return;
    }

    LOG(WARNING) << thread_message_loops_.size() << " threads haven't finished"
                 << " flush in time. Discarding remaining events of them";
  }
  FinishFlush(flush_count);
}

void TraceLog::AddTraceEvent(
    char phase,
    const unsigned char* category_group_enabled,
    const char* name,
    unsigned long long id,
    int num_args,
    const char** arg_names,
    const unsigned char* arg_types,
    const unsigned long long* arg_values,
    scoped_ptr<ConvertableToTraceFormat> convertable_values[],
    unsigned char flags) {
  int thread_id = static_cast<int>(base::PlatformThread::CurrentId());
  base::TimeTicks now = base::TimeTicks::NowFromSystemTraceTime();
  AddTraceEventWithThreadIdAndTimestamp(phase, category_group_enabled, name, id,
                                        thread_id, now, num_args, arg_names,
                                        arg_types, arg_values,
                                        convertable_values, flags);
}

void TraceLog::AddTraceEventWithThreadIdAndTimestamp(
    char phase,
    const unsigned char* category_group_enabled,
    const char* name,
    unsigned long long id,
    int thread_id,
    const TimeTicks& timestamp,
    int num_args,
    const char** arg_names,
    const unsigned char* arg_types,
    const unsigned long long* arg_values,
    scoped_ptr<ConvertableToTraceFormat> convertable_values[],
    unsigned char flags) {
  DCHECK(name);

  if (flags & TRACE_EVENT_FLAG_MANGLE_ID)
    id ^= process_id_hash_;

#if defined(OS_ANDROID)
  SendToATrace(phase, GetCategoryGroupName(category_group_enabled), name, id,
               num_args, arg_names, arg_types, arg_values, convertable_values,
               flags);
#endif

  if (!IsCategoryGroupEnabled(category_group_enabled))
    return;

  TimeTicks now = timestamp - time_offset_;
  TimeTicks thread_now = ThreadNow();

  NotificationHelper notifier(this);

  ThreadLocalEventBuffer* thread_local_event_buffer = NULL;
  // A ThreadLocalEventBuffer needs the message loop
  // - to know when the thread exits;
  // - to handle the final flush.
  // For a thread without a message loop, the trace events will be added into
  // the main buffer directly.
  if (MessageLoop::current()) {
    thread_local_event_buffer = thread_local_event_buffer_.Get();
    if (!thread_local_event_buffer) {
      thread_local_event_buffer = new ThreadLocalEventBuffer(this);
      thread_local_event_buffer_.Set(thread_local_event_buffer);
    }
  }

  // Check and update the current thread name only if the event is for the
  // current thread to avoid locks in most cases.
  if (thread_id == static_cast<int>(PlatformThread::CurrentId())) {
    const char* new_name = ThreadIdNameManager::GetInstance()->
        GetName(thread_id);
    // Check if the thread name has been set or changed since the previous
    // call (if any), but don't bother if the new name is empty. Note this will
    // not detect a thread name change within the same char* buffer address: we
    // favor common case performance over corner case correctness.
    if (new_name != g_current_thread_name.Get().Get() &&
        new_name && *new_name) {
      g_current_thread_name.Get().Set(new_name);

      AutoLock lock(lock_);
      hash_map<int, std::string>::iterator existing_name =
          thread_names_.find(thread_id);
      if (existing_name == thread_names_.end()) {
        // This is a new thread id, and a new name.
        thread_names_[thread_id] = new_name;
      } else {
        // This is a thread id that we've seen before, but potentially with a
        // new name.
        std::vector<StringPiece> existing_names;
        Tokenize(existing_name->second, ",", &existing_names);
        bool found = std::find(existing_names.begin(),
                               existing_names.end(),
                               new_name) != existing_names.end();
        if (!found) {
          existing_name->second.push_back(',');
          existing_name->second.append(new_name);
        }
      }
    }
  }

  if (!subtle::NoBarrier_Load(&buffer_is_full_)) {
    TraceEvent trace_event(thread_id, now, thread_now, phase,
                           category_group_enabled, name, id,
                           num_args, arg_names, arg_types, arg_values,
                           convertable_values, flags);

    if (thread_local_event_buffer) {
      thread_local_event_buffer->AddEvent(trace_event, &notifier);
    } else {
      AutoLock lock(lock_);
      AddEventToMainBufferWhileLocked(trace_event);
      CheckIfBufferIsFullWhileLocked(&notifier);
    }

    if (trace_options() & ECHO_TO_CONSOLE) {
      AutoLock lock(lock_);

      TimeDelta duration;
      if (phase == TRACE_EVENT_PHASE_END) {
        duration = timestamp - thread_event_start_times_[thread_id].top();
        thread_event_start_times_[thread_id].pop();
      }

      std::string thread_name = thread_names_[thread_id];
      if (thread_colors_.find(thread_name) == thread_colors_.end())
        thread_colors_[thread_name] = (thread_colors_.size() % 6) + 1;

      std::ostringstream log;
      log << base::StringPrintf("%s: \x1b[0;3%dm",
                                thread_name.c_str(),
                                thread_colors_[thread_name]);

      size_t depth = 0;
      if (thread_event_start_times_.find(thread_id) !=
          thread_event_start_times_.end())
        depth = thread_event_start_times_[thread_id].size();

      for (size_t i = 0; i < depth; ++i)
        log << "| ";

      trace_event.AppendPrettyPrinted(&log);
      if (phase == TRACE_EVENT_PHASE_END)
        log << base::StringPrintf(" (%.3f ms)", duration.InMillisecondsF());

      LOG(ERROR) << log.str() << "\x1b[0;m";

      if (phase == TRACE_EVENT_PHASE_BEGIN)
        thread_event_start_times_[thread_id].push(timestamp);
    }
  }

  if (reinterpret_cast<const unsigned char*>(subtle::NoBarrier_Load(
      &watch_category_)) == category_group_enabled) {
    AutoLock lock(lock_);
    if (watch_event_name_ == name)
      notifier.AddNotificationWhileLocked(EVENT_WATCH_NOTIFICATION);
  }

  notifier.SendNotificationIfAny();
  EventCallback event_callback = reinterpret_cast<EventCallback>(
      subtle::NoBarrier_Load(&event_callback_));
  if (event_callback) {
    event_callback(phase, category_group_enabled, name, id,
                   num_args, arg_names, arg_types, arg_values,
                   flags);
  }

  if (thread_local_event_buffer)
    thread_local_event_buffer->ReportOverhead(now, thread_now);
}

void TraceLog::AddTraceEventEtw(char phase,
                                const char* name,
                                const void* id,
                                const char* extra) {
#if defined(OS_WIN)
  TraceEventETWProvider::Trace(name, phase, id, extra);
#endif
  INTERNAL_TRACE_EVENT_ADD(phase, "ETW Trace Event", name,
                           TRACE_EVENT_FLAG_COPY, "id", id, "extra", extra);
}

void TraceLog::AddTraceEventEtw(char phase,
                                const char* name,
                                const void* id,
                                const std::string& extra)
{
#if defined(OS_WIN)
  TraceEventETWProvider::Trace(name, phase, id, extra);
#endif
  INTERNAL_TRACE_EVENT_ADD(phase, "ETW Trace Event", name,
                           TRACE_EVENT_FLAG_COPY, "id", id, "extra", extra);
}

void TraceLog::SetWatchEvent(const std::string& category_name,
                             const std::string& event_name) {
  const unsigned char* category = GetCategoryGroupEnabled(
      category_name.c_str());
  size_t notify_count = 0;
  {
    AutoLock lock(lock_);
    subtle::NoBarrier_Store(&watch_category_,
                            reinterpret_cast<subtle::AtomicWord>(category));
    watch_event_name_ = event_name;

    // First, search existing events for watch event because we want to catch
    // it even if it has already occurred.
    notify_count = logged_events_->CountEnabledByName(category, event_name);
  }  // release lock

  // Send notification for each event found.
  for (size_t i = 0; i < notify_count; ++i) {
    NotificationHelper notifier(this);
    lock_.Acquire();
    notifier.AddNotificationWhileLocked(EVENT_WATCH_NOTIFICATION);
    lock_.Release();
    notifier.SendNotificationIfAny();
  }
}

void TraceLog::CancelWatchEvent() {
  AutoLock lock(lock_);
  subtle::NoBarrier_Store(&watch_category_, 0);
  watch_event_name_ = "";
}

namespace {

template <typename T>
void AddMetadataEventToBuffer(
    TraceBuffer* logged_events,
    int thread_id,
    const char* metadata_name, const char* arg_name,
    const T& value) {
  int num_args = 1;
  unsigned char arg_type;
  unsigned long long arg_value;
  trace_event_internal::SetTraceValue(value, &arg_type, &arg_value);
  logged_events->AddEvent(TraceEvent(
      thread_id,
      TimeTicks(), TimeTicks(), TRACE_EVENT_PHASE_METADATA,
      &g_category_group_enabled[g_category_metadata],
      metadata_name, trace_event_internal::kNoEventId,
      num_args, &arg_name, &arg_type, &arg_value, NULL,
      TRACE_EVENT_FLAG_NONE));
}

}

void TraceLog::AddMetadataEvents() {
  lock_.AssertAcquired();

  int current_thread_id = static_cast<int>(base::PlatformThread::CurrentId());
  if (process_sort_index_ != 0) {
    AddMetadataEventToBuffer(logged_events_.get(),
                             current_thread_id,
                             "process_sort_index", "sort_index",
                             process_sort_index_);
  }

  if (process_name_.size()) {
    AddMetadataEventToBuffer(logged_events_.get(),
                             current_thread_id,
                             "process_name", "name",
                             process_name_);
  }

  if (process_labels_.size() > 0) {
    std::vector<std::string> labels;
    for(base::hash_map<int, std::string>::iterator it = process_labels_.begin();
        it != process_labels_.end();
        it++) {
      labels.push_back(it->second);
    }
    AddMetadataEventToBuffer(logged_events_.get(),
                             current_thread_id,
                             "process_labels", "labels",
                             JoinString(labels, ','));
  }

  // Thread sort indices.
  for(hash_map<int, int>::iterator it = thread_sort_indices_.begin();
      it != thread_sort_indices_.end();
      it++) {
    if (it->second == 0)
      continue;
    AddMetadataEventToBuffer(logged_events_.get(),
                             it->first,
                             "thread_sort_index", "sort_index",
                             it->second);
  }

  // Thread names.
  for(hash_map<int, std::string>::iterator it = thread_names_.begin();
      it != thread_names_.end();
      it++) {
    if (it->second.empty())
      continue;
    AddMetadataEventToBuffer(logged_events_.get(),
                             it->first,
                             "thread_name", "name",
                             it->second);
  }
}

void TraceLog::InstallWaitableEventForSamplingTesting(
    WaitableEvent* waitable_event) {
  sampling_thread_->InstallWaitableEventForSamplingTesting(waitable_event);
}

void TraceLog::DeleteForTesting() {
  DeleteTraceLogForTesting::Delete();
}

void TraceLog::SetProcessID(int process_id) {
  process_id_ = process_id;
  // Create a FNV hash from the process ID for XORing.
  // See http://isthe.com/chongo/tech/comp/fnv/ for algorithm details.
  unsigned long long offset_basis = 14695981039346656037ull;
  unsigned long long fnv_prime = 1099511628211ull;
  unsigned long long pid = static_cast<unsigned long long>(process_id_);
  process_id_hash_ = (offset_basis ^ pid) * fnv_prime;
}

void TraceLog::SetProcessSortIndex(int sort_index) {
  AutoLock lock(lock_);
  process_sort_index_ = sort_index;
}

void TraceLog::SetProcessName(const std::string& process_name) {
  AutoLock lock(lock_);
  process_name_ = process_name;
}

void TraceLog::UpdateProcessLabel(
    int label_id, const std::string& current_label) {
  if(!current_label.length())
    return RemoveProcessLabel(label_id);

  AutoLock lock(lock_);
  process_labels_[label_id] = current_label;
}

void TraceLog::RemoveProcessLabel(int label_id) {
  AutoLock lock(lock_);
  base::hash_map<int, std::string>::iterator it = process_labels_.find(
        label_id);
  if (it == process_labels_.end())
    return;

  process_labels_.erase(it);
}

void TraceLog::SetThreadSortIndex(PlatformThreadId thread_id, int sort_index) {
  AutoLock lock(lock_);
  thread_sort_indices_[static_cast<int>(thread_id)] = sort_index;
}

void TraceLog::SetTimeOffset(TimeDelta offset) {
  time_offset_ = offset;
}

size_t TraceLog::GetObserverCountForTest() const {
  return enabled_state_observer_list_.size();
}

bool CategoryFilter::IsEmptyOrContainsLeadingOrTrailingWhitespace(
    const std::string& str) {
  return  str.empty() ||
          str.at(0) == ' ' ||
          str.at(str.length() - 1) == ' ';
}

bool CategoryFilter::DoesCategoryGroupContainCategory(
    const char* category_group,
    const char* category) const {
  DCHECK(category);
  CStringTokenizer category_group_tokens(category_group,
                          category_group + strlen(category_group), ",");
  while (category_group_tokens.GetNext()) {
    std::string category_group_token = category_group_tokens.token();
    // Don't allow empty tokens, nor tokens with leading or trailing space.
    DCHECK(!CategoryFilter::IsEmptyOrContainsLeadingOrTrailingWhitespace(
        category_group_token))
        << "Disallowed category string";
    if (MatchPattern(category_group_token.c_str(), category))
      return true;
  }
  return false;
}

CategoryFilter::CategoryFilter(const std::string& filter_string) {
  if (!filter_string.empty())
    Initialize(filter_string);
  else
    Initialize(CategoryFilter::kDefaultCategoryFilterString);
}

CategoryFilter::CategoryFilter(const CategoryFilter& cf)
    : included_(cf.included_),
      disabled_(cf.disabled_),
      excluded_(cf.excluded_) {
}

CategoryFilter::~CategoryFilter() {
}

CategoryFilter& CategoryFilter::operator=(const CategoryFilter& rhs) {
  if (this == &rhs)
    return *this;

  included_ = rhs.included_;
  disabled_ = rhs.disabled_;
  excluded_ = rhs.excluded_;
  return *this;
}

void CategoryFilter::Initialize(const std::string& filter_string) {
  // Tokenize list of categories, delimited by ','.
  StringTokenizer tokens(filter_string, ",");
  // Add each token to the appropriate list (included_,excluded_).
  while (tokens.GetNext()) {
    std::string category = tokens.token();
    // Ignore empty categories.
    if (category.empty())
      continue;
    // Excluded categories start with '-'.
    if (category.at(0) == '-') {
      // Remove '-' from category string.
      category = category.substr(1);
      excluded_.push_back(category);
    } else if (category.compare(0, strlen(TRACE_DISABLED_BY_DEFAULT("")),
                                TRACE_DISABLED_BY_DEFAULT("")) == 0) {
      disabled_.push_back(category);
    } else {
      included_.push_back(category);
    }
  }
}

void CategoryFilter::WriteString(const StringList& values,
                                 std::string* out,
                                 bool included) const {
  bool prepend_comma = !out->empty();
  int token_cnt = 0;
  for (StringList::const_iterator ci = values.begin();
       ci != values.end(); ++ci) {
    if (token_cnt > 0 || prepend_comma)
      StringAppendF(out, ",");
    StringAppendF(out, "%s%s", (included ? "" : "-"), ci->c_str());
    ++token_cnt;
  }
}

std::string CategoryFilter::ToString() const {
  std::string filter_string;
  WriteString(included_, &filter_string, true);
  WriteString(disabled_, &filter_string, true);
  WriteString(excluded_, &filter_string, false);
  return filter_string;
}

bool CategoryFilter::IsCategoryGroupEnabled(
    const char* category_group_name) const {
  // TraceLog should call this method only as  part of enabling/disabling
  // categories.
  StringList::const_iterator ci;

  // Check the disabled- filters and the disabled-* wildcard first so that a
  // "*" filter does not include the disabled.
  for (ci = disabled_.begin(); ci != disabled_.end(); ++ci) {
    if (DoesCategoryGroupContainCategory(category_group_name, ci->c_str()))
      return true;
  }
  if (DoesCategoryGroupContainCategory(category_group_name,
                                       TRACE_DISABLED_BY_DEFAULT("*")))
    return false;

  for (ci = included_.begin(); ci != included_.end(); ++ci) {
    if (DoesCategoryGroupContainCategory(category_group_name, ci->c_str()))
      return true;
  }

  for (ci = excluded_.begin(); ci != excluded_.end(); ++ci) {
    if (DoesCategoryGroupContainCategory(category_group_name, ci->c_str()))
      return false;
  }
  // If the category group is not excluded, and there are no included patterns
  // we consider this pattern enabled.
  return included_.empty();
}

bool CategoryFilter::HasIncludedPatterns() const {
  return !included_.empty();
}

void CategoryFilter::Merge(const CategoryFilter& nested_filter) {
  // Keep included patterns only if both filters have an included entry.
  // Otherwise, one of the filter was specifying "*" and we want to honour the
  // broadest filter.
  if (HasIncludedPatterns() && nested_filter.HasIncludedPatterns()) {
    included_.insert(included_.end(),
                     nested_filter.included_.begin(),
                     nested_filter.included_.end());
  } else {
    included_.clear();
  }

  disabled_.insert(disabled_.end(),
                   nested_filter.disabled_.begin(),
                   nested_filter.disabled_.end());
  excluded_.insert(excluded_.end(),
                   nested_filter.excluded_.begin(),
                   nested_filter.excluded_.end());
}

void CategoryFilter::Clear() {
  included_.clear();
  disabled_.clear();
  excluded_.clear();
}

}  // namespace debug
}  // namespace base

namespace trace_event_internal {

ScopedTrace::ScopedTrace(
    TRACE_EVENT_API_ATOMIC_WORD* event_uid, const char* name) {
  category_group_enabled_ =
    reinterpret_cast<const unsigned char*>(TRACE_EVENT_API_ATOMIC_LOAD(
        *event_uid));
  if (!category_group_enabled_) {
    category_group_enabled_ = TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED("gpu");
    TRACE_EVENT_API_ATOMIC_STORE(
        *event_uid,
        reinterpret_cast<TRACE_EVENT_API_ATOMIC_WORD>(category_group_enabled_));
  }
  if (*category_group_enabled_) {
    name_ = name;
    TRACE_EVENT_API_ADD_TRACE_EVENT(
        TRACE_EVENT_PHASE_BEGIN,    // phase
        category_group_enabled_,          // category enabled
        name,                       // name
        0,                          // id
        0,                          // num_args
        NULL,                       // arg_names
        NULL,                       // arg_types
        NULL,                       // arg_values
        NULL,                       // convertable_values
        TRACE_EVENT_FLAG_NONE);     // flags
  } else {
    category_group_enabled_ = NULL;
    name_ = NULL;
  }
}

ScopedTrace::~ScopedTrace() {
  if (category_group_enabled_ && *category_group_enabled_) {
    TRACE_EVENT_API_ADD_TRACE_EVENT(
        TRACE_EVENT_PHASE_END,   // phase
        category_group_enabled_,       // category enabled
        name_,                   // name
        0,                       // id
        0,                       // num_args
        NULL,                    // arg_names
        NULL,                    // arg_types
        NULL,                    // arg_values
        NULL,                    // convertable values
        TRACE_EVENT_FLAG_NONE);  // flags
  }
}

}  // namespace trace_event_internal


text:
<?php

namespace Drupal\link\Plugin\migrate\process\d6;

use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\migrate\Plugin\MigrationInterface;
use Drupal\migrate\MigrateExecutableInterface;
use Drupal\migrate\ProcessPluginBase;
use Drupal\migrate\Row;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * @MigrateProcessPlugin(
 *   id = "d6_field_link"
 * )
 */
class FieldLink extends ProcessPluginBase implements ContainerFactoryPluginInterface {

  /**
   * {@inheritdoc}
   */
  public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
    $this->migration = $migration;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration = NULL) {
    return new static(
      $configuration,
      $plugin_id,
      $plugin_definition,
      $migration
    );
  }

  /**
   * Turn a Drupal 6 URI into a Drupal 8-compatible format.
   *
   * @param string $uri
   *   The 'url' value from Drupal 6.
   *
   * @return string
   *   The Drupal 8-compatible URI.
   *
   * @see \Drupal\link\Plugin\Field\FieldWidget\LinkWidget::getUserEnteredStringAsUri()
   */
  protected function canonicalizeUri($uri) {
    // If we already have a scheme, we're fine.
    if (empty($uri) || !is_null(parse_url($uri, PHP_URL_SCHEME))) {
      return $uri;
    }

    // Remove the <front> component of the URL.
    if (strpos($uri, '<front>') === 0) {
      $uri = substr($uri, strlen('<front>'));
    }

    // Add the internal: scheme and ensure a leading slash.
    return 'internal:/' . ltrim($uri, '/');
  }

  /**
   * {@inheritdoc}
   */
  public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
    $attributes = unserialize($value['attributes']);
    // Drupal 6 link attributes might be double serialized.
    if (!is_array($attributes)) {
      $attributes = unserialize($attributes);
    }

    if (!$attributes) {
      $attributes = [];
    }

    // Massage the values into the correct form for the link.
    $route['uri'] = $this->canonicalizeUri($value['url']);
    $route['options']['attributes'] = $attributes;
    $route['title'] = $value['title'];
    return $route;
  }

}

redpajama_set_name: RedPajamaGithub


text:
package br.com.jetpack.packages.system;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

public class LogicalPackage implements Serializable {
	/**
	 *
	 */
	private static final long serialVersionUID = 1L;
	private String name;
	private String[] depends;
	private List<String> dependents = new ArrayList<String>();

	public LogicalPackage(String name, String[] depends) {
		super();
		this.name = name;
		this.depends = depends;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String[] getDepends() {
		return depends;
	}

	public void setDepends(String[] depends) {
		this.depends = depends;
	}

	public String[] getDependents() {
		return dependents.toArray(new String[] {});
	}

	public void addDependents(String dependent) {
		this.dependents.add(dependent);
	}

	public void removeDependents(String dependent) {
		dependents.remove(dependent);
	}

}


text:
package org.apache.flink.table.descriptors;

import org.apache.flink.api.common.typeinfo.Types;
import org.apache.flink.table.api.DataTypes;
import org.apache.flink.table.api.TableSchema;
import org.apache.flink.table.api.ValidationException;
import org.apache.flink.table.types.LogicalTypeParserTest;

import org.junit.Test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;

import static org.junit.Assert.assertEquals;

/**
 * Tests for {@link DescriptorProperties}.
 */
public class DescriptorPropertiesTest {

	private static final String ARRAY_KEY = "my-array";
	private static final String FIXED_INDEXED_PROPERTY_KEY = "my-fixed-indexed-property";
	private static final String PROPERTY_1_KEY = "property-1";
	private static final String PROPERTY_2_KEY = "property-2";

	@Test
	public void testEquals() {
		DescriptorProperties properties1 = new DescriptorProperties();
		properties1.putString("hello1", "12");
		properties1.putString("hello2", "13");
		properties1.putString("hello3", "14");

		DescriptorProperties properties2 = new DescriptorProperties();
		properties2.putString("hello1", "12");
		properties2.putString("hello2", "13");
		properties2.putString("hello3", "14");

		DescriptorProperties properties3 = new DescriptorProperties();
		properties3.putString("hello1", "12");
		properties3.putString("hello3", "14");
		properties3.putString("hello2", "13");

		assertEquals(properties1, properties2);

		assertEquals(properties1, properties3);
	}

	@Test
	public void testMissingArray() {
		DescriptorProperties properties = new DescriptorProperties();

		testArrayValidation(properties, 0, Integer.MAX_VALUE);
	}

	@Test
	public void testArrayValues() {
		DescriptorProperties properties = new DescriptorProperties();

		properties.putString(ARRAY_KEY + ".0", "12");
		properties.putString(ARRAY_KEY + ".1", "42");
		properties.putString(ARRAY_KEY + ".2", "66");

		testArrayValidation(properties, 1, Integer.MAX_VALUE);

		assertEquals(
			Arrays.asList(12, 42, 66),
			properties.getArray(ARRAY_KEY, properties::getInt));
	}

	@Test
	public void testArraySingleValue() {
		DescriptorProperties properties = new DescriptorProperties();
		properties.putString(ARRAY_KEY, "12");

		testArrayValidation(properties, 1, Integer.MAX_VALUE);

		assertEquals(
			Collections.singletonList(12),
			properties.getArray(ARRAY_KEY, properties::getInt));
	}

	@Test(expected = ValidationException.class)
	public void testArrayInvalidValues() {
		DescriptorProperties properties = new DescriptorProperties();
		properties.putString(ARRAY_KEY + ".0", "12");
		properties.putString(ARRAY_KEY + ".1", "66");
		properties.putString(ARRAY_KEY + ".2", "INVALID");

		testArrayValidation(properties, 1, Integer.MAX_VALUE);
	}

	@Test(expected = ValidationException.class)
	public void testArrayInvalidSingleValue() {
		DescriptorProperties properties = new DescriptorProperties();
		properties.putString(ARRAY_KEY, "INVALID");

		testArrayValidation(properties, 1, Integer.MAX_VALUE);
	}

	@Test(expected = ValidationException.class)
	public void testInvalidMissingArray() {
		DescriptorProperties properties = new DescriptorProperties();

		testArrayValidation(properties, 1, Integer.MAX_VALUE);
	}

	@Test(expected = ValidationException.class)
	public void testInvalidFixedIndexedProperties() {
		DescriptorProperties property = new DescriptorProperties();
		List<List<String>> list = new ArrayList<>();
		list.add(Arrays.asList("1", "string"));
		list.add(Arrays.asList("INVALID", "string"));
		property.putIndexedFixedProperties(
			FIXED_INDEXED_PROPERTY_KEY,
			Arrays.asList(PROPERTY_1_KEY, PROPERTY_2_KEY),
			list);
		testFixedIndexedPropertiesValidation(property);
	}

	@Test
	public void testRemoveKeys() {
		DescriptorProperties properties = new DescriptorProperties();
		properties.putString("hello1", "12");
		properties.putString("hello2", "13");
		properties.putString("hello3", "14");

		DescriptorProperties actual = properties.withoutKeys(Arrays.asList("hello1", "hello3"));

		DescriptorProperties expected = new DescriptorProperties();
		expected.putString("hello2", "13");

		assertEquals(expected, actual);
	}

	@Test
	public void testPrefixedMap() {
		DescriptorProperties properties = new DescriptorProperties();
		properties.putString("hello1", "12");
		properties.putString("hello2", "13");
		properties.putString("hello3", "14");

		Map<String, String> actual = properties.asPrefixedMap("prefix.");

		DescriptorProperties expected = new DescriptorProperties();
		expected.putString("prefix.hello1", "12");
		expected.putString("prefix.hello2", "13");
		expected.putString("prefix.hello3", "14");

		assertEquals(expected.asMap(), actual);
	}

	@Test
	public void testTableSchema() {
		TableSchema schema = TableSchema.builder()
			.field("f0", DataTypes.BIGINT().notNull())
			.field("f1", DataTypes.ROW(
				DataTypes.FIELD("q1", DataTypes.STRING()),
				DataTypes.FIELD("q2", DataTypes.TIMESTAMP(9))))
			.field("f2", DataTypes.STRING().notNull())
			.field("f3", DataTypes.BIGINT().notNull(), "f0 + 1")
			.field("f4", DataTypes.DECIMAL(10, 3))
			.watermark(
				"f1.q2",
				"`f1`.`q2` - INTERVAL '5' SECOND",
				DataTypes.TIMESTAMP(3))
			.primaryKey("constraint1", new String[] {"f0", "f2"})
			.build();

		DescriptorProperties properties = new DescriptorProperties();
		properties.putTableSchema("schema", schema);
		Map<String, String> actual = properties.asMap();
		Map<String, String> expected = new HashMap<>();
		expected.put("schema.0.name", "f0");
		expected.put("schema.0.data-type", "BIGINT NOT NULL");
		expected.put("schema.1.name", "f1");
		expected.put("schema.1.data-type", "ROW<`q1` VARCHAR(2147483647), `q2` TIMESTAMP(9)>");
		expected.put("schema.2.name", "f2");
		expected.put("schema.2.data-type", "VARCHAR(2147483647) NOT NULL");
		expected.put("schema.3.name", "f3");
		expected.put("schema.3.data-type", "BIGINT NOT NULL");
		expected.put("schema.3.expr", "f0 + 1");
		expected.put("schema.4.name", "f4");
		expected.put("schema.4.data-type", "DECIMAL(10, 3)");
		expected.put("schema.watermark.0.rowtime", "f1.q2");
		expected.put("schema.watermark.0.strategy.expr", "`f1`.`q2` - INTERVAL '5' SECOND");
		expected.put("schema.watermark.0.strategy.data-type", "TIMESTAMP(3)");
		expected.put("schema.primary-key.name", "constraint1");
		expected.put("schema.primary-key.columns", "f0,f2");
		assertEquals(expected, actual);

		TableSchema restored = properties.getTableSchema("schema");
		assertEquals(schema, restored);
	}

	@Test
	public void testLegacyTableSchema() {
		DescriptorProperties properties = new DescriptorProperties();
		Map<String, String> map = new HashMap<>();
		map.put("schema.0.name", "f0");
		map.put("schema.0.type", "VARCHAR");
		map.put("schema.1.name", "f1");
		map.put("schema.1.type", "BIGINT");
		map.put("schema.2.name", "f2");
		map.put("schema.2.type", "DECIMAL");
		map.put("schema.3.name", "f3");
		map.put("schema.3.type", "TIMESTAMP");
		map.put("schema.4.name", "f4");
		map.put("schema.4.type", "MAP<TINYINT, SMALLINT>");
		map.put("schema.5.name", "f5");
		map.put("schema.5.type", "ANY<java.lang.Class>");
		map.put("schema.6.name", "f6");
		map.put("schema.6.type", "PRIMITIVE_ARRAY<DOUBLE>");
		map.put("schema.7.name", "f7");
		map.put("schema.7.type", "OBJECT_ARRAY<TIME>");
		map.put("schema.8.name", "f8");
		map.put("schema.8.type", "ROW<q1 VARCHAR, q2 DATE>");
		map.put("schema.9.name", "f9");
		map.put("schema.9.type", "POJO<org.apache.flink.table.types.LogicalTypeParserTest$MyPojo>");
		properties.putProperties(map);
		TableSchema restored = properties.getTableSchema("schema");

		TableSchema expected = TableSchema.builder()
			.field("f0", Types.STRING)
			.field("f1", Types.LONG)
			.field("f2", Types.BIG_DEC)
			.field("f3", Types.SQL_TIMESTAMP)
			.field("f4", Types.MAP(Types.BYTE, Types.SHORT))
			.field("f5", Types.GENERIC(Class.class))
			.field("f6", Types.PRIMITIVE_ARRAY(Types.DOUBLE))
			.field("f7", Types.OBJECT_ARRAY(Types.SQL_TIME))
			.field("f8", Types.ROW_NAMED(new String[]{"q1", "q2"}, Types.STRING, Types.SQL_DATE))
			.field("f9", Types.POJO(LogicalTypeParserTest.MyPojo.class))
			.build();

		assertEquals(expected, restored);
	}

	private void testArrayValidation(
		DescriptorProperties properties,
		int minLength,
		int maxLength) {
		Consumer<String> validator = key -> properties.validateInt(key, false);

		properties.validateArray(
			ARRAY_KEY,
			validator,
			minLength,
			maxLength);
	}

	private void testFixedIndexedPropertiesValidation(DescriptorProperties properties) {

		Map<String, Consumer<String>> validatorMap = new HashMap<>();

		// PROPERTY_1 should be Int
		Consumer<String> validator1 = key -> properties.validateInt(key, false);
		validatorMap.put(PROPERTY_1_KEY, validator1);
		// PROPERTY_2 should be String
		Consumer<String> validator2 = key -> properties.validateString(key, false);
		validatorMap.put(PROPERTY_2_KEY, validator2);

		properties.validateFixedIndexedProperties(
			FIXED_INDEXED_PROPERTY_KEY,
			false,
			validatorMap
		);
	}
}


text:
package com.sun.media.imageioimpl.plugins.png;

import java.awt.color.ColorSpace;
import java.awt.color.ICC_ColorSpace;
import java.awt.color.ICC_Profile;
import java.io.InputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import javax.imageio.IIOException;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.spi.ImageReaderSpi;
import com.sun.media.imageioimpl.plugins.clib.CLibImageReader;
import com.sun.medialib.codec.png.Decoder;
import com.sun.medialib.codec.jiio.mediaLibImage;

final class CLibPNGImageReader extends CLibImageReader {
    private Decoder decoder;
    private HashMap imageTypes = new HashMap();

    CLibPNGImageReader(ImageReaderSpi originatingProvider) {
        super(originatingProvider);
    }

    // Implement abstract method defined in superclass.
    protected final synchronized mediaLibImage decode(InputStream stream)
        throws IOException {
        try {
            decoder = new Decoder(stream);
            decoder.decode();
        } catch(Throwable t) {
            throw new IIOException("codecLib error", t);
        }

        /* XXX Get significant bits (sBIT chunk).
        byte[] bits = decoder.getSignificantBits();
        if(bits != null) {
            System.out.println("getSignificantBits():");
            for(int i = 0; i < bits.length; i++) {
                System.out.println((bits[i]&0xff));
            }
        }
        */

        mediaLibImage mlImage = null;
        try {
            mlImage = decoder.getImage();
        } catch(Throwable t) {
            throw new IIOException("codecLib error", t);
        }

        if(mlImage == null) {
            throw new IIOException(I18N.getString("CLibPNGImageReader0"));
        }

        return mlImage;
    }

    public synchronized Iterator getImageTypes(int imageIndex)
        throws IOException {
        seekToImage(imageIndex);

        ArrayList types = null;
        Integer key = new Integer(imageIndex);
        if(imageTypes.containsKey(key)) {
            types = (ArrayList)imageTypes.get(key);
        } else {
            types = new ArrayList();

            // Get the mediaLibImage from the Decoder.
            mediaLibImage image = getImage(imageIndex);

            // Get the palette.
            byte[] rgbPalette = null;
            try {
                // Note: the 'decoder' instance variable is set by
                // decode() which is called by getImage() above.
                rgbPalette = decoder.getPalette();
            } catch(Throwable t) {
                throw new IIOException("codecLib error", t);
            }

            if(rgbPalette != null) {
                // Indexed image: set up the RGB palette arrays.
                int paletteLength = rgbPalette.length/3;
                byte[] r = new byte[paletteLength];
                byte[] g = new byte[paletteLength];
                byte[] b = new byte[paletteLength];
                for(int i = 0, j = 0; i < paletteLength; i++) {
                    r[i] = rgbPalette[j++];
                    g[i] = rgbPalette[j++];
                    b[i] = rgbPalette[j++];
                }

                // Set up the alpha palette array if needed.
                int[] alphaPalette = null;
                try {
                    alphaPalette = decoder.getTransparency();
                } catch(Throwable t) {
                    throw new IIOException("codecLib error", t);
                }
                byte[] a = null;
                if(alphaPalette != null) {
                    // Load beginning of palette from the chunk
                    a = new byte[paletteLength];
                    for(int i = 0; i < alphaPalette.length; i++) {
                        a[i] = (byte)(alphaPalette[i] & 0x000000ff);
                    }

                    // Fill rest of palette with 255
                    for(int i = alphaPalette.length; i < paletteLength; i++) {
                        a[i] = (byte)0xff;
                    }
                }

                types.add(createImageType(image, null, decoder.getBitDepth(),
                                          r, g, b, a));
            } else {
                // Attempt to use the iCCP chunk if present, no sRGB
                // chunk is present, and the ICC color space type matches
                // the image type.
                ColorSpace cs = null;
                if(decoder.getStandardRGB() ==
                   Decoder.PNG_sRGB_NOT_DEFINED) {
                    // Get the profile data.
                    byte[] iccProfileData =
                        decoder.getEmbeddedICCProfile();
                    if(iccProfileData != null) {
                        // Create the ColorSpace.
                        ICC_Profile iccProfile =
                            ICC_Profile.getInstance(iccProfileData);
                        ICC_ColorSpace icccs =
                            new ICC_ColorSpace(iccProfile);

                        // Check the color space type against the
                        // number of bands and the palette.
                        int numBands = image.getChannels();
                        if((icccs.getType() == ColorSpace.TYPE_RGB &&
                            (numBands >= 3 || rgbPalette != null)) ||
                           (icccs.getType() == ColorSpace.TYPE_GRAY &&
                            numBands < 3 && rgbPalette == null)) {
                            cs = icccs;
                        }
                    }
                }

                int bitDepth = decoder.getBitDepth();

                ImageTypeSpecifier type =
                    createImageType(image, cs, bitDepth,
                                    null, null, null, null);
                types.add(type);

                if(type.getColorModel().getColorSpace().equals(cs)) {
                    types.add(createImageType(image, null, bitDepth,
                                              null, null, null, null));
                }
            }

            imageTypes.put(key, types);
        }

        // XXX Need also to use getBackground() to save the background
        // color somewhere, eventually as an image property with the
        // name "background_color" and with a java.awt.Color value.
        // See PNGImageDecoder or the PNG ImageReader for more info.
        // Looks like this needs to be set as a metadata entry. It is
        // obtained from the decoder using getBackground().

        return types.iterator();
    }

    // Override superclass method.
    protected void resetLocal() {
        decoder = null;
        imageTypes.clear();
        super.resetLocal();
    }

    public synchronized IIOMetadata getImageMetadata(int imageIndex)
        throws IIOException {
        if(input == null) {
            throw new IllegalStateException("input == null");
        }
        seekToImage(imageIndex);

        CLibPNGMetadata im = new CLibPNGMetadata();
        try {
            getImage(imageIndex);
        } catch(IOException e) {
            throw new IIOException("codecLib error", e);
        }
        im.readMetadata(this, decoder);
        return im;
    }

    /**
     * Package scope method to classes in package to emit warning messages.
     */
    void forwardWarningMessage(String warning) {
        processWarningOccurred(warning);
    }
}


text:
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Twice.Models.Scheduling;
using Twice.Models.Twitter;

namespace Twice.Tests.Models.Scheduling
{
	[TestClass, ExcludeFromCodeCoverage]
	public class SchedulerTests
	{
		[TestMethod, TestCategory( "Models.Scheduling" )]
		public void FutureJobIsNotExecuted()
		{
			// Arrange
			var contextList = new Mock<ITwitterContextList>();
			var config = new Mock<ITwitterConfiguration>();

			var scheduler = new Scheduler( "invalid.filename", contextList.Object, config.Object );
			var job = new SchedulerJob
			{
				TargetTime = DateTime.Now.AddHours( 1 )
			};

			// Act
			bool processed = scheduler.ProcessJob( job );

			// Assert
			Assert.IsFalse( processed );
		}

		[TestMethod, TestCategory( "Models.Scheduling" )]
		public void JobCanBeAdded()
		{
			var fileName = "SchedulerTests.JobCanBeAdded.json";
			try
			{
				// Arrange
				var contextList = new Mock<ITwitterContextList>();
				var config = new Mock<ITwitterConfiguration>();

				var scheduler = new Scheduler( fileName, contextList.Object, config.Object );

				var job = new SchedulerJob
				{
					JobType = SchedulerJobType.Test,
					AccountIds = new List<ulong> { 123 },
					FilesToAttach = new List<string> { "1", "2", "3" },
					IdsToDelete = new List<ulong> { 456 },
					InReplyToStatus = 678,
					TargetTime = new DateTime( 1, 2, 3, 4, 5, 6 ),
					Text = "hello world"
				};

				// Act
				scheduler.AddJob( job );

				// Assert
				string json = File.ReadAllText( fileName );
				var jobList = JsonConvert.DeserializeObject<List<SchedulerJob>>( json );

				Assert.IsNotNull( jobList );
				Assert.AreEqual( 1, jobList.Count );
				CollectionAssert.AreEquivalent( job.AccountIds, jobList[0].AccountIds );
				Assert.AreEqual( job.InReplyToStatus, jobList[0].InReplyToStatus );
				Assert.AreEqual( job.TargetTime, jobList[0].TargetTime );
				Assert.AreEqual( job.Text, jobList[0].Text );
				Assert.AreEqual( job.JobType, jobList[0].JobType );
				CollectionAssert.AreEquivalent( job.IdsToDelete, jobList[0].IdsToDelete );
				CollectionAssert.AreEquivalent( job.FilesToAttach, jobList[0].FilesToAttach );
			}
			finally
			{
				File.Delete( fileName );
			}
		}

		[TestMethod, TestCategory( "Models.Scheduling" )]
		public void JobIsExecutedInThread()
		{
			// Arrange
			var contextList = new Mock<ITwitterContextList>();
			var config = new Mock<ITwitterConfiguration>();

			var waitHandle = new ManualResetEventSlim( false );

			var proc = new Mock<IJobProcessor>();
			proc.Setup( p => p.Process( It.IsAny<SchedulerJob>() ) ).Returns( Task.CompletedTask ).Callback(
				() => waitHandle.Set() ).Verifiable();

			var scheduler = new Scheduler( "SchedulerTests.JobIsExecutedInThread.json", contextList.Object, config.Object,
				proc.Object, 20 );
			scheduler.Start();
			bool set;

			try
			{
				var job = new SchedulerJob
				{
					JobType = SchedulerJobType.Test,
					TargetTime = DateTime.Now.AddMilliseconds( -1 ),
					AccountIds = new List<ulong> { 1 },
					Text = "test"
				};
				scheduler.AddJob( job );

				// Act
				set = waitHandle.Wait( 2000 );
			}
			finally
			{
				scheduler.Stop();
			}

			// Assert
			Assert.IsTrue( set );
			proc.Verify( p => p.Process( It.IsAny<SchedulerJob>() ), Times.Once() );
		}

		[TestMethod, TestCategory( "Models.Scheduling" )]
		public void JobListIsLoadedOnConstruction()
		{
			// Arrange
			var contextList = new Mock<ITwitterContextList>();
			var config = new Mock<ITwitterConfiguration>();

			var fileName = "SchedulerTests.JobListIsLoadedOnConstruction.json";
			var jobList = new List<SchedulerJob>
			{
				new SchedulerJob
				{
					JobType = SchedulerJobType.CreateStatus,
					AccountIds = new List<ulong> {123},
					InReplyToStatus = 444,
					TargetTime = new DateTime( 1, 2, 3, 4, 5, 6 ),
					Text = "hello world",
					FilesToAttach = new List<string> {"123", "456", "789"}
				},
				new SchedulerJob
				{
					JobType = SchedulerJobType.DeleteStatus,
					AccountIds = new List<ulong> {456},
					IdsToDelete = new List<ulong> {5678}
				},
				new SchedulerJob {JobType = SchedulerJobType.Test}
			};
			File.WriteAllText( fileName, JsonConvert.SerializeObject( jobList ) );

			// Act
			var scheduler = new Scheduler( fileName, contextList.Object, config.Object );

			// Assert
			Assert.AreEqual( 3, scheduler.JobList.Count() );
			var create = scheduler.JobList.SingleOrDefault( j => j.JobType == SchedulerJobType.CreateStatus );
			Assert.IsNotNull( create );
			CollectionAssert.AreEquivalent( jobList[0].AccountIds, create.AccountIds );
			Assert.AreEqual( jobList[0].InReplyToStatus, create.InReplyToStatus );
			Assert.AreEqual( jobList[0].TargetTime, create.TargetTime );
			Assert.AreEqual( jobList[0].Text, create.Text );
			CollectionAssert.AreEquivalent( jobList[0].FilesToAttach, create.FilesToAttach );

			var delete = scheduler.JobList.SingleOrDefault( j => j.JobType == SchedulerJobType.DeleteStatus );
			Assert.IsNotNull( delete );
			CollectionAssert.AreEquivalent( jobList[1].AccountIds, delete.AccountIds );
			CollectionAssert.AreEquivalent( jobList[1].IdsToDelete, delete.IdsToDelete );

			var test = scheduler.JobList.SingleOrDefault( j => j.JobType == SchedulerJobType.Test );
			Assert.IsNotNull( test );
		}

		[TestMethod, TestCategory( "Models.Scheduling" )]
		public void LoadingBrokenJobListDoesNotCrash()
		{
			// Arrange
			var contextList = new Mock<ITwitterContextList>();
			var config = new Mock<ITwitterConfiguration>();

			var fileName = "SchedulerTests.LoadingBrokenJobListDoesNotCrash.json";
			File.WriteAllText( fileName, @"This is a broken JSON file" );

			// Act
			// ReSharper disable once ObjectCreationAsStatement
			var ex = ExceptionAssert.Catch<Exception>( () => new Scheduler( fileName, contextList.Object, config.Object ) );

			// Assert
			Assert.IsNull( ex );
		}

		[TestMethod, TestCategory( "Models.Scheduling" )]
		public void PastJobIsExecuted()
		{
			// Arrange
			var contextList = new Mock<ITwitterContextList>();
			var config = new Mock<ITwitterConfiguration>();

			var job = new SchedulerJob
			{
				TargetTime = DateTime.Now.AddHours( -1 )
			};

			var proc = new Mock<IJobProcessor>();
			proc.Setup( p => p.Process( job ) ).Verifiable();
			var scheduler = new Scheduler( "invalid.filename", contextList.Object, config.Object, proc.Object );

			// Act
			bool processed = scheduler.ProcessJob( job );

			// Assert
			Assert.IsTrue( processed );
			proc.Verify( p => p.Process( job ), Times.Once() );
		}
	}
}


text:
<?php
/**
 * Message translations.
 *
 * This file is automatically generated by 'yii message' command.
 * It contains the localizable messages extracted from source code.
 * You may modify this file by translating the extracted messages.
 *
 * Each array element represents the translation (value) of a message (key).
 * If the value is empty, the message is considered as not translated.
 * Messages that no longer need translation will have their translations
 * enclosed between a pair of '@@' marks.
 *
 * Message string can be used with plural forms format. Check i18n section
 * of the guide for details.
 *
 * NOTE: this file must be saved in UTF-8 encoding.
 */
return [
    'Add post' => '',
    'After connect with VKontakte enter code in URL: {link}' => '',
    'All operation with your social account can execute only by you!' => '',
    'Attention' => '',
    'Connect to social network' => '',
    'Content' => '',
    'Getting started' => '',
    'No one can\'t collect or post message to your account in social network!' => '',
    'Post' => '',
    'Posts' => '',
    'Record are deleted successfully!' => '',
    'Records are deleted already.' => '',
    'Remove all records' => '',
    'Social networks' => '',
    'Status' => '',
    'Submit' => '',
    'Sync with social networks' => '',
    'There\'s Yii2-SyncSocial Extension is configurated with <nobr>syncDelete = false</nobr>' => '',
    'There\'s an example application of extension usage for Active Record model' => '',
    'Time Created' => '',
    'Time Published' => '',
    'Time Updated' => '',
    'Update post' => '',
    'Work with Posts' => '',
    'Work with posts' => '',
    'Yii2 SyncSocial Extension' => '',
    'that means after post deleting from social network will not be deleted' => '',
    'Add sign' => '@@@@',
    'Services' => '@@@@',
    'Signs' => '@@@@',
    'Verify code' => '@@@@',
    'Your e-mail' => '@@@@',
    'all' => '@@@@',
    'month' => '@@@@',
    'today' => '@@@@',
    'week' => '@@@@',
];


text:
/*!
 * \file   sync_frontend.hpp
 * \author Andrey Semashev
 * \date   14.07.2009
 *
 * The header contains implementation of synchronous sink frontend.
 */

#ifndef BOOST_LOG_SINKS_SYNC_FRONTEND_HPP_INCLUDED_
#define BOOST_LOG_SINKS_SYNC_FRONTEND_HPP_INCLUDED_

#include <boost/log/detail/config.hpp>

#ifdef BOOST_HAS_PRAGMA_ONCE
#pragma once
#endif

#if defined(BOOST_LOG_NO_THREADS)
#error Boost.Log: Synchronous sink frontend is only supported in multithreaded environment
#endif

#include <boost/static_assert.hpp>
#include <boost/smart_ptr/shared_ptr.hpp>
#include <boost/smart_ptr/make_shared_object.hpp>
#include <boost/preprocessor/control/if.hpp>
#include <boost/preprocessor/comparison/equal.hpp>
#include <boost/thread/recursive_mutex.hpp>
#include <boost/log/detail/locking_ptr.hpp>
#include <boost/log/detail/parameter_tools.hpp>
#include <boost/log/core/record_view.hpp>
#include <boost/log/sinks/basic_sink_frontend.hpp>
#include <boost/log/sinks/frontend_requirements.hpp>
#include <boost/log/detail/header.hpp>

namespace boost {

BOOST_LOG_OPEN_NAMESPACE

namespace sinks {

#ifndef BOOST_LOG_DOXYGEN_PASS

#define BOOST_LOG_SINK_CTOR_FORWARD_INTERNAL_1(n, data)\
    template< typename T0 >\
    explicit synchronous_sink(T0 const& arg0, typename boost::log::aux::enable_if_named_parameters< T0, boost::log::aux::sfinae_dummy >::type = boost::log::aux::sfinae_dummy()) :\
        base_type(false),\
        m_pBackend(boost::make_shared< sink_backend_type >(arg0)) {}

#define BOOST_LOG_SINK_CTOR_FORWARD_INTERNAL_N(n, data)\
    template< BOOST_PP_ENUM_PARAMS(n, typename T) >\
    explicit synchronous_sink(BOOST_PP_ENUM_BINARY_PARAMS(n, T, const& arg)) :\
        base_type(false),\
        m_pBackend(boost::make_shared< sink_backend_type >(BOOST_PP_ENUM_PARAMS(n, arg))) {}

#define BOOST_LOG_SINK_CTOR_FORWARD_INTERNAL(z, n, data)\
    BOOST_PP_IF(BOOST_PP_EQUAL(n, 1), BOOST_LOG_SINK_CTOR_FORWARD_INTERNAL_1, BOOST_LOG_SINK_CTOR_FORWARD_INTERNAL_N)(n, data)

#endif // BOOST_LOG_DOXYGEN_PASS

/*!
 * \brief Synchronous logging sink frontend
 *
 * The sink frontend serializes threads before passing logging records to the backend
 */
template< typename SinkBackendT >
class synchronous_sink :
    public aux::make_sink_frontend_base< SinkBackendT >::type
{
    typedef typename aux::make_sink_frontend_base< SinkBackendT >::type base_type;

private:
    //! Synchronization mutex type
    typedef boost::recursive_mutex backend_mutex_type;

public:
    //! Sink implementation type
    typedef SinkBackendT sink_backend_type;
    //! \cond
    BOOST_STATIC_ASSERT_MSG((has_requirement< typename sink_backend_type::frontend_requirements, synchronized_feeding >::value), "Synchronous sink frontend is incompatible with the specified backend: thread synchronization requirements are not met");
    //! \endcond

#ifndef BOOST_LOG_DOXYGEN_PASS

    //! A pointer type that locks the backend until it's destroyed
    typedef boost::log::aux::locking_ptr< sink_backend_type, backend_mutex_type > locked_backend_ptr;

#else // BOOST_LOG_DOXYGEN_PASS

    //! A pointer type that locks the backend until it's destroyed
    typedef implementation_defined locked_backend_ptr;

#endif // BOOST_LOG_DOXYGEN_PASS

private:
    //! Synchronization mutex
    backend_mutex_type m_BackendMutex;
    //! Pointer to the backend
    const shared_ptr< sink_backend_type > m_pBackend;

public:
    /*!
     * Default constructor. Constructs the sink backend instance.
     * Requires the backend to be default-constructible.
     */
    synchronous_sink() :
        base_type(false),
        m_pBackend(boost::make_shared< sink_backend_type >())
    {
    }
    /*!
     * Constructor attaches user-constructed backend instance
     *
     * \param backend Pointer to the backend instance
     *
     * \pre \a backend is not \c NULL.
     */
    explicit synchronous_sink(shared_ptr< sink_backend_type > const& backend) :
        base_type(false),
        m_pBackend(backend)
    {
    }

    /*!
     * Constructor that passes arbitrary named parameters to the interprocess sink backend constructor.
     * Refer to the backend documentation for the list of supported parameters.
     */
#ifndef BOOST_LOG_DOXYGEN_PASS
    BOOST_LOG_PARAMETRIZED_CONSTRUCTORS_GEN(BOOST_LOG_SINK_CTOR_FORWARD_INTERNAL, ~)
#else
    template< typename... Args >
    explicit synchronous_sink(Args&&... args);
#endif

    /*!
     * Locking accessor to the attached backend
     */
    locked_backend_ptr locked_backend()
    {
        return locked_backend_ptr(m_pBackend, m_BackendMutex);
    }

    /*!
     * Passes the log record to the backend
     */
    void consume(record_view const& rec) BOOST_OVERRIDE
    {
        base_type::feed_record(rec, m_BackendMutex, *m_pBackend);
    }

    /*!
     * The method attempts to pass logging record to the backend
     */
    bool try_consume(record_view const& rec) BOOST_OVERRIDE
    {
        return base_type::try_feed_record(rec, m_BackendMutex, *m_pBackend);
    }

    /*!
     * The method performs flushing of any internal buffers that may hold log records. The method
     * may take considerable time to complete and may block both the calling thread and threads
     * attempting to put new records into the sink while this call is in progress.
     */
    void flush() BOOST_OVERRIDE
    {
        base_type::flush_backend(m_BackendMutex, *m_pBackend);
    }
};

#undef BOOST_LOG_SINK_CTOR_FORWARD_INTERNAL_1
#undef BOOST_LOG_SINK_CTOR_FORWARD_INTERNAL_N
#undef BOOST_LOG_SINK_CTOR_FORWARD_INTERNAL

} // namespace sinks

BOOST_LOG_CLOSE_NAMESPACE // namespace log

} // namespace boost

#include <boost/log/detail/footer.hpp>

#endif // BOOST_LOG_SINKS_SYNC_FRONTEND_HPP_INCLUDED_