Skip to content
This repository has been archived by the owner on Apr 3, 2020. It is now read-only.

Commit

Permalink
Create libsampler as V8 sampler library.
Browse files Browse the repository at this point in the history
This patch does five things:

1. Extracts sampler as libsampler to provide sampling functionality support.
2. Makes SampleStack virtual so embedders can override the behaviour of sample collecting.
3. Removes sampler.[h|cc].
4. Moves sampling thread into log.cc as workaround to keep the --prof functionality.
5. Creates SamplerManager to manage the relationship between samplers and threads.

The reason we port hashmap.h is that in debug mode, STL containers are using
mutexes from a mutex pool, which may lead to deadlock when using asynchronously
signal handler.

Currently libsampler is used in V8 temporarily.

BUG=v8:4789
LOG=n

Review-Url: https://codereview.chromium.org/1922303002
Cr-Commit-Position: refs/heads/master@{#36527}
  • Loading branch information
lpy authored and Commit bot committed May 25, 2016
1 parent d84dbc7 commit 06cc9b7
Show file tree
Hide file tree
Showing 19 changed files with 1,294 additions and 878 deletions.
25 changes: 23 additions & 2 deletions BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ config("libplatform_config") {
include_dirs = [ "include" ]
}

# This config should be applied to code using the libsampler.
config("libsampler_config") {
include_dirs = [ "include" ]
}

# This config should only be applied to code using V8 and not any V8 code
# itself.
config("external_config") {
Expand Down Expand Up @@ -1329,8 +1334,6 @@ v8_source_set("v8_base") {
"src/profiler/profile-generator-inl.h",
"src/profiler/profile-generator.cc",
"src/profiler/profile-generator.h",
"src/profiler/sampler.cc",
"src/profiler/sampler.h",
"src/profiler/sampling-heap-profiler.cc",
"src/profiler/sampling-heap-profiler.h",
"src/profiler/strings-storage.cc",
Expand Down Expand Up @@ -1816,6 +1819,7 @@ v8_source_set("v8_base") {
defines = []
deps = [
":v8_libbase",
":v8_libsampler",
]

if (is_win) {
Expand Down Expand Up @@ -1970,6 +1974,23 @@ v8_source_set("v8_libplatform") {
]
}

v8_source_set("v8_libsampler") {
sources = [
"src/libsampler/hashmap.h",
"src/libsampler/utils.h",
"src/libsampler/v8-sampler.cc",
"src/libsampler/v8-sampler.h",
]

configs = [ ":internal_config_base" ]

public_configs = [ ":libsampler_config" ]

deps = [
":v8_libbase",
]
}

v8_source_set("fuzzer_support") {
visibility = [ ":*" ] # Only targets in this file can depend on this.

Expand Down
6 changes: 6 additions & 0 deletions include/v8.h
Original file line number Diff line number Diff line change
Expand Up @@ -6331,6 +6331,12 @@ class V8_EXPORT Isolate {
*/
void VisitWeakHandles(PersistentHandleVisitor* visitor);

/**
* Check if this isolate is in use.
* True if at least one thread Enter'ed this isolate.
*/
bool IsInUse();

private:
template <class K, class V, class Traits>
friend class PersistentValueMapBase;
Expand Down
6 changes: 6 additions & 0 deletions src/api.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7793,6 +7793,12 @@ void Isolate::VisitExternalResources(ExternalResourceVisitor* visitor) {
}


bool Isolate::IsInUse() {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(this);
return isolate->IsInUse();
}


class VisitorAdapter : public i::ObjectVisitor {
public:
explicit VisitorAdapter(PersistentHandleVisitor* visitor)
Expand Down
4 changes: 2 additions & 2 deletions src/isolate.cc
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@
#include "src/ic/stub-cache.h"
#include "src/interpreter/interpreter.h"
#include "src/isolate-inl.h"
#include "src/libsampler/v8-sampler.h"
#include "src/log.h"
#include "src/messages.h"
#include "src/profiler/cpu-profiler.h"
#include "src/profiler/sampler.h"
#include "src/prototype.h"
#include "src/regexp/regexp-stack.h"
#include "src/runtime-profiler.h"
Expand Down Expand Up @@ -2006,7 +2006,7 @@ void Isolate::Deinit() {
}

// We must stop the logger before we tear down other components.
Sampler* sampler = logger_->sampler();
sampler::Sampler* sampler = logger_->sampler();
if (sampler && sampler->IsActive()) sampler->Stop();

delete deoptimizer_data_;
Expand Down
6 changes: 6 additions & 0 deletions src/libsampler/DEPS
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
include_rules = [
"+include",
"-src",
"+src/base",
"+src/libsampler",
]
278 changes: 278 additions & 0 deletions src/libsampler/hashmap.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,278 @@
// Copyright 2012 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// This file is ported from src/hashmap.h

#ifndef V8_LIBSAMPLER_HASHMAP_H_
#define V8_LIBSAMPLER_HASHMAP_H_

#include "src/base/bits.h"
#include "src/base/logging.h"
#include "src/libsampler/utils.h"

namespace v8 {
namespace sampler {

class HashMapImpl {
public:
typedef bool (*MatchFun) (void* key1, void* key2);

// The default capacity.
static const uint32_t kDefaultHashMapCapacity = 8;

// initial_capacity is the size of the initial hash map;
// it must be a power of 2 (and thus must not be 0).
HashMapImpl(MatchFun match,
uint32_t capacity = kDefaultHashMapCapacity);

~HashMapImpl();

// HashMap entries are (key, value, hash) triplets.
// Some clients may not need to use the value slot
// (e.g. implementers of sets, where the key is the value).
struct Entry {
void* key;
void* value;
uint32_t hash; // The full hash value for key
int order; // If you never remove entries this is the insertion order.
};

// If an entry with matching key is found, returns that entry.
// Otherwise, NULL is returned.
Entry* Lookup(void* key, uint32_t hash) const;

// If an entry with matching key is found, returns that entry.
// If no matching entry is found, a new entry is inserted with
// corresponding key, key hash, and NULL value.
Entry* LookupOrInsert(void* key, uint32_t hash);

// Removes the entry with matching key.
// It returns the value of the deleted entry
// or null if there is no value for such key.
void* Remove(void* key, uint32_t hash);

// Empties the hash map (occupancy() == 0).
void Clear();

// The number of (non-empty) entries in the table.
uint32_t occupancy() const { return occupancy_; }

// The capacity of the table. The implementation
// makes sure that occupancy is at most 80% of
// the table capacity.
uint32_t capacity() const { return capacity_; }

// Iteration
//
// for (Entry* p = map.Start(); p != NULL; p = map.Next(p)) {
// ...
// }
//
// If entries are inserted during iteration, the effect of
// calling Next() is undefined.
Entry* Start() const;
Entry* Next(Entry* p) const;

// Some match functions defined for convenience.
static bool PointersMatch(void* key1, void* key2) {
return key1 == key2;
}

private:
MatchFun match_;
Entry* map_;
uint32_t capacity_;
uint32_t occupancy_;

Entry* map_end() const { return map_ + capacity_; }
Entry* Probe(void* key, uint32_t hash) const;
void Initialize(uint32_t capacity);
void Resize();
};

typedef HashMapImpl HashMap;

HashMapImpl::HashMapImpl(MatchFun match, uint32_t initial_capacity) {
match_ = match;
Initialize(initial_capacity);
}


HashMapImpl::~HashMapImpl() {
Malloced::Delete(map_);
}


HashMapImpl::Entry* HashMapImpl::Lookup(void* key, uint32_t hash) const {
Entry* p = Probe(key, hash);
return p->key != NULL ? p : NULL;
}


HashMapImpl::Entry* HashMapImpl::LookupOrInsert(void* key, uint32_t hash) {
// Find a matching entry.
Entry* p = Probe(key, hash);
if (p->key != NULL) {
return p;
}

// No entry found; insert one.
p->key = key;
p->value = NULL;
p->hash = hash;
p->order = occupancy_;
occupancy_++;

// Grow the map if we reached >= 80% occupancy.
if (occupancy_ + occupancy_ / 4 >= capacity_) {
Resize();
p = Probe(key, hash);
}

return p;
}


void* HashMapImpl::Remove(void* key, uint32_t hash) {
// Lookup the entry for the key to remove.
Entry* p = Probe(key, hash);
if (p->key == NULL) {
// Key not found nothing to remove.
return NULL;
}

void* value = p->value;
// To remove an entry we need to ensure that it does not create an empty
// entry that will cause the search for another entry to stop too soon. If all
// the entries between the entry to remove and the next empty slot have their
// initial position inside this interval, clearing the entry to remove will
// not break the search. If, while searching for the next empty entry, an
// entry is encountered which does not have its initial position between the
// entry to remove and the position looked at, then this entry can be moved to
// the place of the entry to remove without breaking the search for it. The
// entry made vacant by this move is now the entry to remove and the process
// starts over.
// Algorithm from http://en.wikipedia.org/wiki/Open_addressing.

// This guarantees loop termination as there is at least one empty entry so
// eventually the removed entry will have an empty entry after it.
DCHECK(occupancy_ < capacity_);

// p is the candidate entry to clear. q is used to scan forwards.
Entry* q = p; // Start at the entry to remove.
while (true) {
// Move q to the next entry.
q = q + 1;
if (q == map_end()) {
q = map_;
}

// All entries between p and q have their initial position between p and q
// and the entry p can be cleared without breaking the search for these
// entries.
if (q->key == NULL) {
break;
}

// Find the initial position for the entry at position q.
Entry* r = map_ + (q->hash & (capacity_ - 1));

// If the entry at position q has its initial position outside the range
// between p and q it can be moved forward to position p and will still be
// found. There is now a new candidate entry for clearing.
if ((q > p && (r <= p || r > q)) ||
(q < p && (r <= p && r > q))) {
*p = *q;
p = q;
}
}

// Clear the entry which is allowed to en emptied.
p->key = NULL;
occupancy_--;
return value;
}


void HashMapImpl::Clear() {
// Mark all entries as empty.
const Entry* end = map_end();
for (Entry* p = map_; p < end; p++) {
p->key = NULL;
}
occupancy_ = 0;
}


HashMapImpl::Entry* HashMapImpl::Start() const {
return Next(map_ - 1);
}


HashMapImpl::Entry* HashMapImpl::Next(Entry* p) const {
const Entry* end = map_end();
DCHECK(map_ - 1 <= p && p < end);
for (p++; p < end; p++) {
if (p->key != NULL) {
return p;
}
}
return NULL;
}


HashMapImpl::Entry* HashMapImpl::Probe(void* key, uint32_t hash) const {
DCHECK(key != NULL);

DCHECK(base::bits::IsPowerOfTwo32(capacity_));
Entry* p = map_ + (hash & (capacity_ - 1));
const Entry* end = map_end();
DCHECK(map_ <= p && p < end);

DCHECK(occupancy_ < capacity_); // Guarantees loop termination.
while (p->key != NULL && (hash != p->hash || !match_(key, p->key))) {
p++;
if (p >= end) {
p = map_;
}
}

return p;
}


void HashMapImpl::Initialize(uint32_t capacity) {
DCHECK(base::bits::IsPowerOfTwo32(capacity));
map_ = reinterpret_cast<Entry*>(Malloced::New(capacity * sizeof(Entry)));
CHECK(map_ != NULL);
capacity_ = capacity;
Clear();
}


void HashMapImpl::Resize() {
Entry* map = map_;
uint32_t n = occupancy_;

// Allocate larger map.
Initialize(capacity_ * 2);

// Rehash all current entries.
for (Entry* p = map; n > 0; p++) {
if (p->key != NULL) {
Entry* entry = LookupOrInsert(p->key, p->hash);
entry->value = p->value;
entry->order = p->order;
n--;
}
}

// Delete old map.
Malloced::Delete(map);
}

} // namespace sampler
} // namespace v8

#endif // V8_LIBSAMPLER_HASHMAP_H_
Loading

0 comments on commit 06cc9b7

Please sign in to comment.