mirror of
https://github.com/nodejs/node.git
synced 2024-11-21 10:59:27 +00:00
perf_hooks: implement histogram based api
Add a sampling-based event loop delay monitor. ```js const { monitorEventLoopDelay } = require('perf_hooks'); const h = monitorEventLoopDelay(); h.enable(); h.disable(); console.log(h.percentiles); console.log(h.min); console.log(h.max); console.log(h.mean); console.log(h.stddev); console.log(h.percentile(50)); ``` PR-URL: https://github.com/nodejs/node/pull/25378 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Gireesh Punathil <gpunathi@in.ibm.com> Reviewed-By: Stephen Belanger <admin@stephenbelanger.com> Reviewed-By: Richard Lau <riclau@uk.ibm.com> Reviewed-By: Anna Henningsen <anna@addaleax.net>
This commit is contained in:
parent
679c23f2ae
commit
bcdd228f90
45
LICENSE
45
LICENSE
@ -1371,3 +1371,48 @@ The externally maintained libraries used by Node.js are:
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
"""
|
||||
|
||||
- HdrHistogram, located at deps/histogram, is licensed as follows:
|
||||
"""
|
||||
The code in this repository code was Written by Gil Tene, Michael Barker,
|
||||
and Matt Warren, and released to the public domain, as explained at
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
|
||||
For users of this code who wish to consume it under the "BSD" license
|
||||
rather than under the public domain or CC0 contribution text mentioned
|
||||
above, the code found under this directory is *also* provided under the
|
||||
following license (commonly referred to as the BSD 2-Clause License). This
|
||||
license does not detract from the above stated release of the code into
|
||||
the public domain, and simply represents an additional license granted by
|
||||
the Author.
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
** Beginning of "BSD 2-Clause License" text. **
|
||||
|
||||
Copyright (c) 2012, 2013, 2014 Gil Tene
|
||||
Copyright (c) 2014 Michael Barker
|
||||
Copyright (c) 2014 Matt Warren
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
THE POSSIBILITY OF SUCH DAMAGE.
|
||||
"""
|
||||
|
41
deps/histogram/LICENSE.txt
vendored
Normal file
41
deps/histogram/LICENSE.txt
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
The code in this repository code was Written by Gil Tene, Michael Barker,
|
||||
and Matt Warren, and released to the public domain, as explained at
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
|
||||
For users of this code who wish to consume it under the "BSD" license
|
||||
rather than under the public domain or CC0 contribution text mentioned
|
||||
above, the code found under this directory is *also* provided under the
|
||||
following license (commonly referred to as the BSD 2-Clause License). This
|
||||
license does not detract from the above stated release of the code into
|
||||
the public domain, and simply represents an additional license granted by
|
||||
the Author.
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
** Beginning of "BSD 2-Clause License" text. **
|
||||
|
||||
Copyright (c) 2012, 2013, 2014 Gil Tene
|
||||
Copyright (c) 2014 Michael Barker
|
||||
Copyright (c) 2014 Matt Warren
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
THE POSSIBILITY OF SUCH DAMAGE.
|
3
deps/histogram/README.md
vendored
Normal file
3
deps/histogram/README.md
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
# HdrHistogram_c
|
||||
|
||||
From: https://github.com/HdrHistogram/HdrHistogram_c
|
15
deps/histogram/histogram.gyp
vendored
Normal file
15
deps/histogram/histogram.gyp
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
'targets': [
|
||||
{
|
||||
'target_name': 'histogram',
|
||||
'type': 'static_library',
|
||||
'include_dirs': ['src'],
|
||||
'direct_dependent_settings': {
|
||||
'include_dirs': [ 'src' ]
|
||||
},
|
||||
'sources': [
|
||||
'src/hdr_histogram.c',
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
1032
deps/histogram/src/hdr_histogram.c
vendored
Normal file
1032
deps/histogram/src/hdr_histogram.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
434
deps/histogram/src/hdr_histogram.h
vendored
Normal file
434
deps/histogram/src/hdr_histogram.h
vendored
Normal file
@ -0,0 +1,434 @@
|
||||
/**
|
||||
* hdr_histogram.h
|
||||
* Written by Michael Barker and released to the public domain,
|
||||
* as explained at http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*
|
||||
* The source for the hdr_histogram utilises a few C99 constructs, specifically
|
||||
* the use of stdint/stdbool and inline variable declaration.
|
||||
*/
|
||||
|
||||
#ifndef HDR_HISTOGRAM_H
|
||||
#define HDR_HISTOGRAM_H 1
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
struct hdr_histogram
|
||||
{
|
||||
int64_t lowest_trackable_value;
|
||||
int64_t highest_trackable_value;
|
||||
int32_t unit_magnitude;
|
||||
int32_t significant_figures;
|
||||
int32_t sub_bucket_half_count_magnitude;
|
||||
int32_t sub_bucket_half_count;
|
||||
int64_t sub_bucket_mask;
|
||||
int32_t sub_bucket_count;
|
||||
int32_t bucket_count;
|
||||
int64_t min_value;
|
||||
int64_t max_value;
|
||||
int32_t normalizing_index_offset;
|
||||
double conversion_ratio;
|
||||
int32_t counts_len;
|
||||
int64_t total_count;
|
||||
int64_t* counts;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Allocate the memory and initialise the hdr_histogram.
|
||||
*
|
||||
* Due to the size of the histogram being the result of some reasonably
|
||||
* involved math on the input parameters this function it is tricky to stack allocate.
|
||||
* The histogram should be released with hdr_close
|
||||
*
|
||||
* @param lowest_trackable_value The smallest possible value to be put into the
|
||||
* histogram.
|
||||
* @param highest_trackable_value The largest possible value to be put into the
|
||||
* histogram.
|
||||
* @param significant_figures The level of precision for this histogram, i.e. the number
|
||||
* of figures in a decimal number that will be maintained. E.g. a value of 3 will mean
|
||||
* the results from the histogram will be accurate up to the first three digits. Must
|
||||
* be a value between 1 and 5 (inclusive).
|
||||
* @param result Output parameter to capture allocated histogram.
|
||||
* @return 0 on success, EINVAL if lowest_trackable_value is < 1 or the
|
||||
* significant_figure value is outside of the allowed range, ENOMEM if malloc
|
||||
* failed.
|
||||
*/
|
||||
int hdr_init(
|
||||
int64_t lowest_trackable_value,
|
||||
int64_t highest_trackable_value,
|
||||
int significant_figures,
|
||||
struct hdr_histogram** result);
|
||||
|
||||
/**
|
||||
* Free the memory and close the hdr_histogram.
|
||||
*
|
||||
* @param h The histogram you want to close.
|
||||
*/
|
||||
void hdr_close(struct hdr_histogram* h);
|
||||
|
||||
/**
|
||||
* Allocate the memory and initialise the hdr_histogram. This is the equivalent of calling
|
||||
* hdr_init(1, highest_trackable_value, significant_figures, result);
|
||||
*
|
||||
* @deprecated use hdr_init.
|
||||
*/
|
||||
int hdr_alloc(int64_t highest_trackable_value, int significant_figures, struct hdr_histogram** result);
|
||||
|
||||
|
||||
/**
|
||||
* Reset a histogram to zero - empty out a histogram and re-initialise it
|
||||
*
|
||||
* If you want to re-use an existing histogram, but reset everything back to zero, this
|
||||
* is the routine to use.
|
||||
*
|
||||
* @param h The histogram you want to reset to empty.
|
||||
*
|
||||
*/
|
||||
void hdr_reset(struct hdr_histogram* h);
|
||||
|
||||
/**
|
||||
* Get the memory size of the hdr_histogram.
|
||||
*
|
||||
* @param h "This" pointer
|
||||
* @return The amount of memory used by the hdr_histogram in bytes
|
||||
*/
|
||||
size_t hdr_get_memory_size(struct hdr_histogram* h);
|
||||
|
||||
/**
|
||||
* Records a value in the histogram, will round this value of to a precision at or better
|
||||
* than the significant_figure specified at construction time.
|
||||
*
|
||||
* @param h "This" pointer
|
||||
* @param value Value to add to the histogram
|
||||
* @return false if the value is larger than the highest_trackable_value and can't be recorded,
|
||||
* true otherwise.
|
||||
*/
|
||||
bool hdr_record_value(struct hdr_histogram* h, int64_t value);
|
||||
|
||||
/**
|
||||
* Records count values in the histogram, will round this value of to a
|
||||
* precision at or better than the significant_figure specified at construction
|
||||
* time.
|
||||
*
|
||||
* @param h "This" pointer
|
||||
* @param value Value to add to the histogram
|
||||
* @param count Number of 'value's to add to the histogram
|
||||
* @return false if any value is larger than the highest_trackable_value and can't be recorded,
|
||||
* true otherwise.
|
||||
*/
|
||||
bool hdr_record_values(struct hdr_histogram* h, int64_t value, int64_t count);
|
||||
|
||||
|
||||
/**
|
||||
* Record a value in the histogram and backfill based on an expected interval.
|
||||
*
|
||||
* Records a value in the histogram, will round this value of to a precision at or better
|
||||
* than the significant_figure specified at contruction time. This is specifically used
|
||||
* for recording latency. If the value is larger than the expected_interval then the
|
||||
* latency recording system has experienced co-ordinated omission. This method fills in the
|
||||
* values that would have occured had the client providing the load not been blocked.
|
||||
|
||||
* @param h "This" pointer
|
||||
* @param value Value to add to the histogram
|
||||
* @param expected_interval The delay between recording values.
|
||||
* @return false if the value is larger than the highest_trackable_value and can't be recorded,
|
||||
* true otherwise.
|
||||
*/
|
||||
bool hdr_record_corrected_value(struct hdr_histogram* h, int64_t value, int64_t expexcted_interval);
|
||||
/**
|
||||
* Record a value in the histogram 'count' times. Applies the same correcting logic
|
||||
* as 'hdr_record_corrected_value'.
|
||||
*
|
||||
* @param h "This" pointer
|
||||
* @param value Value to add to the histogram
|
||||
* @param count Number of 'value's to add to the histogram
|
||||
* @param expected_interval The delay between recording values.
|
||||
* @return false if the value is larger than the highest_trackable_value and can't be recorded,
|
||||
* true otherwise.
|
||||
*/
|
||||
bool hdr_record_corrected_values(struct hdr_histogram* h, int64_t value, int64_t count, int64_t expected_interval);
|
||||
|
||||
/**
|
||||
* Adds all of the values from 'from' to 'this' histogram. Will return the
|
||||
* number of values that are dropped when copying. Values will be dropped
|
||||
* if they around outside of h.lowest_trackable_value and
|
||||
* h.highest_trackable_value.
|
||||
*
|
||||
* @param h "This" pointer
|
||||
* @param from Histogram to copy values from.
|
||||
* @return The number of values dropped when copying.
|
||||
*/
|
||||
int64_t hdr_add(struct hdr_histogram* h, const struct hdr_histogram* from);
|
||||
|
||||
/**
|
||||
* Adds all of the values from 'from' to 'this' histogram. Will return the
|
||||
* number of values that are dropped when copying. Values will be dropped
|
||||
* if they around outside of h.lowest_trackable_value and
|
||||
* h.highest_trackable_value.
|
||||
*
|
||||
* @param h "This" pointer
|
||||
* @param from Histogram to copy values from.
|
||||
* @return The number of values dropped when copying.
|
||||
*/
|
||||
int64_t hdr_add_while_correcting_for_coordinated_omission(
|
||||
struct hdr_histogram* h, struct hdr_histogram* from, int64_t expected_interval);
|
||||
|
||||
/**
|
||||
* Get minimum value from the histogram. Will return 2^63-1 if the histogram
|
||||
* is empty.
|
||||
*
|
||||
* @param h "This" pointer
|
||||
*/
|
||||
int64_t hdr_min(const struct hdr_histogram* h);
|
||||
|
||||
/**
|
||||
* Get maximum value from the histogram. Will return 0 if the histogram
|
||||
* is empty.
|
||||
*
|
||||
* @param h "This" pointer
|
||||
*/
|
||||
int64_t hdr_max(const struct hdr_histogram* h);
|
||||
|
||||
/**
|
||||
* Get the value at a specific percentile.
|
||||
*
|
||||
* @param h "This" pointer.
|
||||
* @param percentile The percentile to get the value for
|
||||
*/
|
||||
int64_t hdr_value_at_percentile(const struct hdr_histogram* h, double percentile);
|
||||
|
||||
/**
|
||||
* Gets the standard deviation for the values in the histogram.
|
||||
*
|
||||
* @param h "This" pointer
|
||||
* @return The standard deviation
|
||||
*/
|
||||
double hdr_stddev(const struct hdr_histogram* h);
|
||||
|
||||
/**
|
||||
* Gets the mean for the values in the histogram.
|
||||
*
|
||||
* @param h "This" pointer
|
||||
* @return The mean
|
||||
*/
|
||||
double hdr_mean(const struct hdr_histogram* h);
|
||||
|
||||
/**
|
||||
* Determine if two values are equivalent with the histogram's resolution.
|
||||
* Where "equivalent" means that value samples recorded for any two
|
||||
* equivalent values are counted in a common total count.
|
||||
*
|
||||
* @param h "This" pointer
|
||||
* @param a first value to compare
|
||||
* @param b second value to compare
|
||||
* @return 'true' if values are equivalent with the histogram's resolution.
|
||||
*/
|
||||
bool hdr_values_are_equivalent(const struct hdr_histogram* h, int64_t a, int64_t b);
|
||||
|
||||
/**
|
||||
* Get the lowest value that is equivalent to the given value within the histogram's resolution.
|
||||
* Where "equivalent" means that value samples recorded for any two
|
||||
* equivalent values are counted in a common total count.
|
||||
*
|
||||
* @param h "This" pointer
|
||||
* @param value The given value
|
||||
* @return The lowest value that is equivalent to the given value within the histogram's resolution.
|
||||
*/
|
||||
int64_t hdr_lowest_equivalent_value(const struct hdr_histogram* h, int64_t value);
|
||||
|
||||
/**
|
||||
* Get the count of recorded values at a specific value
|
||||
* (to within the histogram resolution at the value level).
|
||||
*
|
||||
* @param h "This" pointer
|
||||
* @param value The value for which to provide the recorded count
|
||||
* @return The total count of values recorded in the histogram within the value range that is
|
||||
* {@literal >=} lowestEquivalentValue(<i>value</i>) and {@literal <=} highestEquivalentValue(<i>value</i>)
|
||||
*/
|
||||
int64_t hdr_count_at_value(const struct hdr_histogram* h, int64_t value);
|
||||
|
||||
int64_t hdr_count_at_index(const struct hdr_histogram* h, int32_t index);
|
||||
|
||||
int64_t hdr_value_at_index(const struct hdr_histogram* h, int32_t index);
|
||||
|
||||
struct hdr_iter_percentiles
|
||||
{
|
||||
bool seen_last_value;
|
||||
int32_t ticks_per_half_distance;
|
||||
double percentile_to_iterate_to;
|
||||
double percentile;
|
||||
};
|
||||
|
||||
struct hdr_iter_recorded
|
||||
{
|
||||
int64_t count_added_in_this_iteration_step;
|
||||
};
|
||||
|
||||
struct hdr_iter_linear
|
||||
{
|
||||
int64_t value_units_per_bucket;
|
||||
int64_t count_added_in_this_iteration_step;
|
||||
int64_t next_value_reporting_level;
|
||||
int64_t next_value_reporting_level_lowest_equivalent;
|
||||
};
|
||||
|
||||
struct hdr_iter_log
|
||||
{
|
||||
double log_base;
|
||||
int64_t count_added_in_this_iteration_step;
|
||||
int64_t next_value_reporting_level;
|
||||
int64_t next_value_reporting_level_lowest_equivalent;
|
||||
};
|
||||
|
||||
/**
|
||||
* The basic iterator. This is a generic structure
|
||||
* that supports all of the types of iteration. Use
|
||||
* the appropriate initialiser to get the desired
|
||||
* iteration.
|
||||
*
|
||||
* @
|
||||
*/
|
||||
struct hdr_iter
|
||||
{
|
||||
const struct hdr_histogram* h;
|
||||
/** raw index into the counts array */
|
||||
int32_t counts_index;
|
||||
/** snapshot of the length at the time the iterator is created */
|
||||
int32_t total_count;
|
||||
/** value directly from array for the current counts_index */
|
||||
int64_t count;
|
||||
/** sum of all of the counts up to and including the count at this index */
|
||||
int64_t cumulative_count;
|
||||
/** The current value based on counts_index */
|
||||
int64_t value;
|
||||
int64_t highest_equivalent_value;
|
||||
int64_t lowest_equivalent_value;
|
||||
int64_t median_equivalent_value;
|
||||
int64_t value_iterated_from;
|
||||
int64_t value_iterated_to;
|
||||
|
||||
union
|
||||
{
|
||||
struct hdr_iter_percentiles percentiles;
|
||||
struct hdr_iter_recorded recorded;
|
||||
struct hdr_iter_linear linear;
|
||||
struct hdr_iter_log log;
|
||||
} specifics;
|
||||
|
||||
bool (* _next_fp)(struct hdr_iter* iter);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Initalises the basic iterator.
|
||||
*
|
||||
* @param itr 'This' pointer
|
||||
* @param h The histogram to iterate over
|
||||
*/
|
||||
void hdr_iter_init(struct hdr_iter* iter, const struct hdr_histogram* h);
|
||||
|
||||
/**
|
||||
* Initialise the iterator for use with percentiles.
|
||||
*/
|
||||
void hdr_iter_percentile_init(struct hdr_iter* iter, const struct hdr_histogram* h, int32_t ticks_per_half_distance);
|
||||
|
||||
/**
|
||||
* Initialise the iterator for use with recorded values.
|
||||
*/
|
||||
void hdr_iter_recorded_init(struct hdr_iter* iter, const struct hdr_histogram* h);
|
||||
|
||||
/**
|
||||
* Initialise the iterator for use with linear values.
|
||||
*/
|
||||
void hdr_iter_linear_init(
|
||||
struct hdr_iter* iter,
|
||||
const struct hdr_histogram* h,
|
||||
int64_t value_units_per_bucket);
|
||||
|
||||
/**
|
||||
* Initialise the iterator for use with logarithmic values
|
||||
*/
|
||||
void hdr_iter_log_init(
|
||||
struct hdr_iter* iter,
|
||||
const struct hdr_histogram* h,
|
||||
int64_t value_units_first_bucket,
|
||||
double log_base);
|
||||
|
||||
/**
|
||||
* Iterate to the next value for the iterator. If there are no more values
|
||||
* available return faluse.
|
||||
*
|
||||
* @param itr 'This' pointer
|
||||
* @return 'false' if there are no values remaining for this iterator.
|
||||
*/
|
||||
bool hdr_iter_next(struct hdr_iter* iter);
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CLASSIC,
|
||||
CSV
|
||||
} format_type;
|
||||
|
||||
/**
|
||||
* Print out a percentile based histogram to the supplied stream. Note that
|
||||
* this call will not flush the FILE, this is left up to the user.
|
||||
*
|
||||
* @param h 'This' pointer
|
||||
* @param stream The FILE to write the output to
|
||||
* @param ticks_per_half_distance The number of iteration steps per half-distance to 100%
|
||||
* @param value_scale Scale the output values by this amount
|
||||
* @param format_type Format to use, e.g. CSV.
|
||||
* @return 0 on success, error code on failure. EIO if an error occurs writing
|
||||
* the output.
|
||||
*/
|
||||
int hdr_percentiles_print(
|
||||
struct hdr_histogram* h, FILE* stream, int32_t ticks_per_half_distance,
|
||||
double value_scale, format_type format);
|
||||
|
||||
/**
|
||||
* Internal allocation methods, used by hdr_dbl_histogram.
|
||||
*/
|
||||
struct hdr_histogram_bucket_config
|
||||
{
|
||||
int64_t lowest_trackable_value;
|
||||
int64_t highest_trackable_value;
|
||||
int64_t unit_magnitude;
|
||||
int64_t significant_figures;
|
||||
int32_t sub_bucket_half_count_magnitude;
|
||||
int32_t sub_bucket_half_count;
|
||||
int64_t sub_bucket_mask;
|
||||
int32_t sub_bucket_count;
|
||||
int32_t bucket_count;
|
||||
int32_t counts_len;
|
||||
};
|
||||
|
||||
int hdr_calculate_bucket_config(
|
||||
int64_t lowest_trackable_value,
|
||||
int64_t highest_trackable_value,
|
||||
int significant_figures,
|
||||
struct hdr_histogram_bucket_config* cfg);
|
||||
|
||||
void hdr_init_preallocated(struct hdr_histogram* h, struct hdr_histogram_bucket_config* cfg);
|
||||
|
||||
int64_t hdr_size_of_equivalent_value_range(const struct hdr_histogram* h, int64_t value);
|
||||
|
||||
int64_t hdr_next_non_equivalent_value(const struct hdr_histogram* h, int64_t value);
|
||||
|
||||
int64_t hdr_median_equivalent_value(const struct hdr_histogram* h, int64_t value);
|
||||
|
||||
/**
|
||||
* Used to reset counters after importing data manuallying into the histogram, used by the logging code
|
||||
* and other custom serialisation tools.
|
||||
*/
|
||||
void hdr_reset_internal_counters(struct hdr_histogram* h);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
22
deps/histogram/src/hdr_tests.h
vendored
Normal file
22
deps/histogram/src/hdr_tests.h
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
#ifndef HDR_TESTS_H
|
||||
#define HDR_TESTS_H
|
||||
|
||||
/* These are functions used in tests and are not intended for normal usage. */
|
||||
|
||||
#include "hdr_histogram.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int32_t counts_index_for(const struct hdr_histogram* h, int64_t value);
|
||||
int hdr_encode_compressed(struct hdr_histogram* h, uint8_t** compressed_histogram, size_t* compressed_len);
|
||||
int hdr_decode_compressed(uint8_t* buffer, size_t length, struct hdr_histogram** histogram);
|
||||
void hdr_base64_decode_block(const char* input, uint8_t* output);
|
||||
void hdr_base64_encode_block(const uint8_t* input, char* output);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
@ -398,6 +398,119 @@ Returns a list of `PerformanceEntry` objects in chronological order
|
||||
with respect to `performanceEntry.startTime` whose `performanceEntry.entryType`
|
||||
is equal to `type`.
|
||||
|
||||
## monitorEventLoopDelay([options])
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
* `options` {Object}
|
||||
* `resolution` {number} The sampling rate in milliseconds. Must be greater
|
||||
than zero. Defaults to `10`.
|
||||
* Returns: {Histogram}
|
||||
|
||||
Creates a `Histogram` object that samples and reports the event loop delay
|
||||
over time.
|
||||
|
||||
Using a timer to detect approximate event loop delay works because the
|
||||
execution of timers is tied specifically to the lifecycle of the libuv
|
||||
event loop. That is, a delay in the loop will cause a delay in the execution
|
||||
of the timer, and those delays are specifically what this API is intended to
|
||||
detect.
|
||||
|
||||
```js
|
||||
const { monitorEventLoopDelay } = require('perf_hooks');
|
||||
const h = monitorEventLoopDelay({ resolution: 20 });
|
||||
h.enable();
|
||||
// Do something
|
||||
h.disable();
|
||||
console.log(h.min);
|
||||
console.log(h.max);
|
||||
console.log(h.mean);
|
||||
console.log(h.stddev);
|
||||
console.log(h.percentiles);
|
||||
console.log(h.percentile(50));
|
||||
console.log(h.percentile(99));
|
||||
```
|
||||
|
||||
### Class: Histogram
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
Tracks the event loop delay at a given sampling rate.
|
||||
|
||||
#### histogram.disable()
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
* Returns: {boolean}
|
||||
|
||||
Disables the event loop delay sample timer. Returns `true` if the timer was
|
||||
stopped, `false` if it was already stopped.
|
||||
|
||||
#### histogram.enable()
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
* Returns: {boolean}
|
||||
|
||||
Enables the event loop delay sample timer. Returns `true` if the timer was
|
||||
started, `false` if it was already started.
|
||||
|
||||
#### histogram.exceeds
|
||||
|
||||
* Value: {number}
|
||||
|
||||
The number of times the event loop delay exceeded the maximum 1 hour event
|
||||
loop delay threshold.
|
||||
|
||||
#### histogram.max
|
||||
|
||||
* Value: {number}
|
||||
|
||||
The maximum recorded event loop delay.
|
||||
|
||||
#### histogram.mean
|
||||
|
||||
* Value: {number}
|
||||
|
||||
The mean of the recorded event loop delays.
|
||||
|
||||
#### histogram.min
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
* Value: {number}
|
||||
|
||||
The minimum recorded event loop delay.
|
||||
|
||||
#### histogram.percentile(percentile)
|
||||
|
||||
* `percentile` {number} A percentile value between 1 and 100.
|
||||
|
||||
Returns the value at the given percentile.
|
||||
|
||||
#### histogram.percentiles
|
||||
|
||||
* Value: {Map}
|
||||
|
||||
Returns a `Map` object detailing the accumulated percentile distribution.
|
||||
|
||||
#### histogram.reset()
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
Resets the collected histogram data.
|
||||
|
||||
#### histogram.stddev
|
||||
|
||||
* Value: {number}
|
||||
|
||||
The standard deviation of the recorded event loop delays.
|
||||
|
||||
## Examples
|
||||
|
||||
### Measuring the duration of async operations
|
||||
|
@ -1,6 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const {
|
||||
ELDHistogram: _ELDHistogram,
|
||||
PerformanceEntry,
|
||||
mark: _mark,
|
||||
clearMark: _clearMark,
|
||||
@ -35,6 +36,8 @@ const { AsyncResource } = require('async_hooks');
|
||||
const L = require('internal/linkedlist');
|
||||
const kInspect = require('internal/util').customInspectSymbol;
|
||||
|
||||
const kHandle = Symbol('handle');
|
||||
const kMap = Symbol('map');
|
||||
const kCallback = Symbol('callback');
|
||||
const kTypes = Symbol('types');
|
||||
const kEntries = Symbol('entries');
|
||||
@ -545,9 +548,73 @@ function sortedInsert(list, entry) {
|
||||
list.splice(location, 0, entry);
|
||||
}
|
||||
|
||||
class ELDHistogram {
|
||||
constructor(handle) {
|
||||
this[kHandle] = handle;
|
||||
this[kMap] = new Map();
|
||||
}
|
||||
|
||||
reset() { this[kHandle].reset(); }
|
||||
enable() { return this[kHandle].enable(); }
|
||||
disable() { return this[kHandle].disable(); }
|
||||
|
||||
get exceeds() { return this[kHandle].exceeds(); }
|
||||
get min() { return this[kHandle].min(); }
|
||||
get max() { return this[kHandle].max(); }
|
||||
get mean() { return this[kHandle].mean(); }
|
||||
get stddev() { return this[kHandle].stddev(); }
|
||||
percentile(percentile) {
|
||||
if (typeof percentile !== 'number') {
|
||||
const errors = lazyErrors();
|
||||
throw new errors.ERR_INVALID_ARG_TYPE('percentile', 'number', percentile);
|
||||
}
|
||||
if (percentile <= 0 || percentile > 100) {
|
||||
const errors = lazyErrors();
|
||||
throw new errors.ERR_INVALID_ARG_VALUE.RangeError('percentile',
|
||||
percentile);
|
||||
}
|
||||
return this[kHandle].percentile(percentile);
|
||||
}
|
||||
get percentiles() {
|
||||
this[kMap].clear();
|
||||
this[kHandle].percentiles(this[kMap]);
|
||||
return this[kMap];
|
||||
}
|
||||
|
||||
[kInspect]() {
|
||||
return {
|
||||
min: this.min,
|
||||
max: this.max,
|
||||
mean: this.mean,
|
||||
stddev: this.stddev,
|
||||
percentiles: this.percentiles,
|
||||
exceeds: this.exceeds
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function monitorEventLoopDelay(options = {}) {
|
||||
if (typeof options !== 'object' || options === null) {
|
||||
const errors = lazyErrors();
|
||||
throw new errors.ERR_INVALID_ARG_TYPE('options', 'Object', options);
|
||||
}
|
||||
const { resolution = 10 } = options;
|
||||
if (typeof resolution !== 'number') {
|
||||
const errors = lazyErrors();
|
||||
throw new errors.ERR_INVALID_ARG_TYPE('options.resolution',
|
||||
'number', resolution);
|
||||
}
|
||||
if (resolution <= 0 || !Number.isSafeInteger(resolution)) {
|
||||
const errors = lazyErrors();
|
||||
throw new errors.ERR_INVALID_OPT_VALUE.RangeError('resolution', resolution);
|
||||
}
|
||||
return new ELDHistogram(new _ELDHistogram(resolution));
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
performance,
|
||||
PerformanceObserver
|
||||
PerformanceObserver,
|
||||
monitorEventLoopDelay
|
||||
};
|
||||
|
||||
Object.defineProperty(module.exports, 'constants', {
|
||||
|
7
node.gyp
7
node.gyp
@ -261,8 +261,9 @@
|
||||
],
|
||||
'include_dirs': [
|
||||
'src',
|
||||
'deps/v8/include',
|
||||
'deps/v8/include'
|
||||
],
|
||||
'dependencies': [ 'deps/histogram/histogram.gyp:histogram' ],
|
||||
|
||||
# - "C4244: conversion from 'type1' to 'type2', possible loss of data"
|
||||
# Ususaly safe. Disable for `dep`, enable for `src`
|
||||
@ -360,6 +361,7 @@
|
||||
'src',
|
||||
'<(SHARED_INTERMEDIATE_DIR)' # for node_natives.h
|
||||
],
|
||||
'dependencies': [ 'deps/histogram/histogram.gyp:histogram' ],
|
||||
|
||||
'sources': [
|
||||
'src/api/callback.cc',
|
||||
@ -458,6 +460,8 @@
|
||||
'src/env.h',
|
||||
'src/env-inl.h',
|
||||
'src/handle_wrap.h',
|
||||
'src/histogram.h',
|
||||
'src/histogram-inl.h',
|
||||
'src/http_parser_adaptor.h',
|
||||
'src/js_stream.h',
|
||||
'src/memory_tracker.h',
|
||||
@ -966,6 +970,7 @@
|
||||
'<(node_lib_target_name)',
|
||||
'rename_node_bin_win',
|
||||
'deps/gtest/gtest.gyp:gtest',
|
||||
'deps/histogram/histogram.gyp:histogram',
|
||||
'node_dtrace_header',
|
||||
'node_dtrace_ustack',
|
||||
'node_dtrace_provider',
|
||||
|
@ -233,6 +233,7 @@
|
||||
[ 'OS=="aix"', {
|
||||
'defines': [
|
||||
'_LINUX_SOURCE_COMPAT',
|
||||
'__STDC_FORMAT_MACROS'
|
||||
],
|
||||
'conditions': [
|
||||
[ 'force_load=="true"', {
|
||||
|
63
src/histogram-inl.h
Normal file
63
src/histogram-inl.h
Normal file
@ -0,0 +1,63 @@
|
||||
#ifndef SRC_HISTOGRAM_INL_H_
|
||||
#define SRC_HISTOGRAM_INL_H_
|
||||
|
||||
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
||||
|
||||
#include "histogram.h"
|
||||
#include "node_internals.h"
|
||||
|
||||
namespace node {
|
||||
|
||||
inline Histogram::Histogram(int64_t lowest, int64_t highest, int figures) {
|
||||
CHECK_EQ(0, hdr_init(lowest, highest, figures, &histogram_));
|
||||
}
|
||||
|
||||
inline Histogram::~Histogram() {
|
||||
hdr_close(histogram_);
|
||||
}
|
||||
|
||||
inline void Histogram::Reset() {
|
||||
hdr_reset(histogram_);
|
||||
}
|
||||
|
||||
inline bool Histogram::Record(int64_t value) {
|
||||
return hdr_record_value(histogram_, value);
|
||||
}
|
||||
|
||||
inline int64_t Histogram::Min() {
|
||||
return hdr_min(histogram_);
|
||||
}
|
||||
|
||||
inline int64_t Histogram::Max() {
|
||||
return hdr_max(histogram_);
|
||||
}
|
||||
|
||||
inline double Histogram::Mean() {
|
||||
return hdr_mean(histogram_);
|
||||
}
|
||||
|
||||
inline double Histogram::Stddev() {
|
||||
return hdr_stddev(histogram_);
|
||||
}
|
||||
|
||||
inline double Histogram::Percentile(double percentile) {
|
||||
CHECK_GT(percentile, 0);
|
||||
CHECK_LE(percentile, 100);
|
||||
return hdr_value_at_percentile(histogram_, percentile);
|
||||
}
|
||||
|
||||
inline void Histogram::Percentiles(std::function<void(double, double)> fn) {
|
||||
hdr_iter iter;
|
||||
hdr_iter_percentile_init(&iter, histogram_, 1);
|
||||
while (hdr_iter_next(&iter)) {
|
||||
double key = iter.specifics.percentiles.percentile;
|
||||
double value = iter.value;
|
||||
fn(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace node
|
||||
|
||||
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
||||
|
||||
#endif // SRC_HISTOGRAM_INL_H_
|
38
src/histogram.h
Normal file
38
src/histogram.h
Normal file
@ -0,0 +1,38 @@
|
||||
#ifndef SRC_HISTOGRAM_H_
|
||||
#define SRC_HISTOGRAM_H_
|
||||
|
||||
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
||||
|
||||
#include "hdr_histogram.h"
|
||||
#include <functional>
|
||||
#include <map>
|
||||
|
||||
namespace node {
|
||||
|
||||
class Histogram {
|
||||
public:
|
||||
inline Histogram(int64_t lowest, int64_t highest, int figures = 3);
|
||||
inline virtual ~Histogram();
|
||||
|
||||
inline bool Record(int64_t value);
|
||||
inline void Reset();
|
||||
inline int64_t Min();
|
||||
inline int64_t Max();
|
||||
inline double Mean();
|
||||
inline double Stddev();
|
||||
inline double Percentile(double percentile);
|
||||
inline void Percentiles(std::function<void(double, double)> fn);
|
||||
|
||||
size_t GetMemorySize() const {
|
||||
return hdr_get_memory_size(histogram_);
|
||||
}
|
||||
|
||||
private:
|
||||
hdr_histogram* histogram_;
|
||||
};
|
||||
|
||||
} // namespace node
|
||||
|
||||
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
||||
|
||||
#endif // SRC_HISTOGRAM_H_
|
186
src/node_perf.cc
186
src/node_perf.cc
@ -1,5 +1,10 @@
|
||||
#include "aliased_buffer.h"
|
||||
#include "node_internals.h"
|
||||
#include "node_perf.h"
|
||||
#include "node_buffer.h"
|
||||
#include "node_process.h"
|
||||
|
||||
#include <cinttypes>
|
||||
|
||||
#ifdef __POSIX__
|
||||
#include <sys/time.h> // gettimeofday
|
||||
@ -20,6 +25,7 @@ using v8::HandleScope;
|
||||
using v8::Integer;
|
||||
using v8::Isolate;
|
||||
using v8::Local;
|
||||
using v8::Map;
|
||||
using v8::MaybeLocal;
|
||||
using v8::Name;
|
||||
using v8::NewStringType;
|
||||
@ -387,6 +393,168 @@ void Timerify(const FunctionCallbackInfo<Value>& args) {
|
||||
args.GetReturnValue().Set(wrap);
|
||||
}
|
||||
|
||||
// Event Loop Timing Histogram
|
||||
namespace {
|
||||
static void ELDHistogramMin(const FunctionCallbackInfo<Value>& args) {
|
||||
ELDHistogram* histogram;
|
||||
ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
|
||||
double value = static_cast<double>(histogram->Min());
|
||||
args.GetReturnValue().Set(value);
|
||||
}
|
||||
|
||||
static void ELDHistogramMax(const FunctionCallbackInfo<Value>& args) {
|
||||
ELDHistogram* histogram;
|
||||
ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
|
||||
double value = static_cast<double>(histogram->Max());
|
||||
args.GetReturnValue().Set(value);
|
||||
}
|
||||
|
||||
static void ELDHistogramMean(const FunctionCallbackInfo<Value>& args) {
|
||||
ELDHistogram* histogram;
|
||||
ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
|
||||
args.GetReturnValue().Set(histogram->Mean());
|
||||
}
|
||||
|
||||
static void ELDHistogramExceeds(const FunctionCallbackInfo<Value>& args) {
|
||||
ELDHistogram* histogram;
|
||||
ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
|
||||
double value = static_cast<double>(histogram->Exceeds());
|
||||
args.GetReturnValue().Set(value);
|
||||
}
|
||||
|
||||
static void ELDHistogramStddev(const FunctionCallbackInfo<Value>& args) {
|
||||
ELDHistogram* histogram;
|
||||
ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
|
||||
args.GetReturnValue().Set(histogram->Stddev());
|
||||
}
|
||||
|
||||
static void ELDHistogramPercentile(const FunctionCallbackInfo<Value>& args) {
|
||||
ELDHistogram* histogram;
|
||||
ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
|
||||
CHECK(args[0]->IsNumber());
|
||||
double percentile = args[0].As<Number>()->Value();
|
||||
args.GetReturnValue().Set(histogram->Percentile(percentile));
|
||||
}
|
||||
|
||||
static void ELDHistogramPercentiles(const FunctionCallbackInfo<Value>& args) {
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
ELDHistogram* histogram;
|
||||
ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
|
||||
CHECK(args[0]->IsMap());
|
||||
Local<Map> map = args[0].As<Map>();
|
||||
histogram->Percentiles([&](double key, double value) {
|
||||
map->Set(env->context(),
|
||||
Number::New(env->isolate(), key),
|
||||
Number::New(env->isolate(), value)).IsEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
static void ELDHistogramEnable(const FunctionCallbackInfo<Value>& args) {
|
||||
ELDHistogram* histogram;
|
||||
ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
|
||||
args.GetReturnValue().Set(histogram->Enable());
|
||||
}
|
||||
|
||||
static void ELDHistogramDisable(const FunctionCallbackInfo<Value>& args) {
|
||||
ELDHistogram* histogram;
|
||||
ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
|
||||
args.GetReturnValue().Set(histogram->Disable());
|
||||
}
|
||||
|
||||
static void ELDHistogramReset(const FunctionCallbackInfo<Value>& args) {
|
||||
ELDHistogram* histogram;
|
||||
ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
|
||||
histogram->ResetState();
|
||||
}
|
||||
|
||||
static void ELDHistogramNew(const FunctionCallbackInfo<Value>& args) {
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
CHECK(args.IsConstructCall());
|
||||
int32_t resolution = args[0]->IntegerValue(env->context()).FromJust();
|
||||
CHECK_GT(resolution, 0);
|
||||
new ELDHistogram(env, args.This(), resolution);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
ELDHistogram::ELDHistogram(
|
||||
Environment* env,
|
||||
Local<Object> wrap,
|
||||
int32_t resolution) : BaseObject(env, wrap),
|
||||
Histogram(1, 3.6e12),
|
||||
resolution_(resolution) {
|
||||
MakeWeak();
|
||||
timer_ = new uv_timer_t();
|
||||
uv_timer_init(env->event_loop(), timer_);
|
||||
timer_->data = this;
|
||||
}
|
||||
|
||||
void ELDHistogram::CloseTimer() {
|
||||
if (timer_ == nullptr)
|
||||
return;
|
||||
|
||||
env()->CloseHandle(timer_, [](uv_timer_t* handle) { delete handle; });
|
||||
timer_ = nullptr;
|
||||
}
|
||||
|
||||
ELDHistogram::~ELDHistogram() {
|
||||
Disable();
|
||||
CloseTimer();
|
||||
}
|
||||
|
||||
void ELDHistogramDelayInterval(uv_timer_t* req) {
|
||||
ELDHistogram* histogram =
|
||||
reinterpret_cast<ELDHistogram*>(req->data);
|
||||
histogram->RecordDelta();
|
||||
TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
|
||||
"min", histogram->Min());
|
||||
TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
|
||||
"max", histogram->Max());
|
||||
TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
|
||||
"mean", histogram->Mean());
|
||||
TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
|
||||
"stddev", histogram->Stddev());
|
||||
}
|
||||
|
||||
bool ELDHistogram::RecordDelta() {
|
||||
uint64_t time = uv_hrtime();
|
||||
bool ret = true;
|
||||
if (prev_ > 0) {
|
||||
int64_t delta = time - prev_;
|
||||
if (delta > 0) {
|
||||
ret = Record(delta);
|
||||
TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
|
||||
"delay", delta);
|
||||
if (!ret) {
|
||||
if (exceeds_ < 0xFFFFFFFF)
|
||||
exceeds_++;
|
||||
ProcessEmitWarning(
|
||||
env(),
|
||||
"Event loop delay exceeded 1 hour: %" PRId64 " nanoseconds",
|
||||
delta);
|
||||
}
|
||||
}
|
||||
}
|
||||
prev_ = time;
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool ELDHistogram::Enable() {
|
||||
if (enabled_) return false;
|
||||
enabled_ = true;
|
||||
uv_timer_start(timer_,
|
||||
ELDHistogramDelayInterval,
|
||||
resolution_,
|
||||
resolution_);
|
||||
uv_unref(reinterpret_cast<uv_handle_t*>(timer_));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ELDHistogram::Disable() {
|
||||
if (!enabled_) return false;
|
||||
enabled_ = false;
|
||||
uv_timer_stop(timer_);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Initialize(Local<Object> target,
|
||||
Local<Value> unused,
|
||||
@ -456,6 +624,24 @@ void Initialize(Local<Object> target,
|
||||
env->constants_string(),
|
||||
constants,
|
||||
attr).ToChecked();
|
||||
|
||||
Local<String> eldh_classname = FIXED_ONE_BYTE_STRING(isolate, "ELDHistogram");
|
||||
Local<FunctionTemplate> eldh =
|
||||
env->NewFunctionTemplate(ELDHistogramNew);
|
||||
eldh->SetClassName(eldh_classname);
|
||||
eldh->InstanceTemplate()->SetInternalFieldCount(1);
|
||||
env->SetProtoMethod(eldh, "exceeds", ELDHistogramExceeds);
|
||||
env->SetProtoMethod(eldh, "min", ELDHistogramMin);
|
||||
env->SetProtoMethod(eldh, "max", ELDHistogramMax);
|
||||
env->SetProtoMethod(eldh, "mean", ELDHistogramMean);
|
||||
env->SetProtoMethod(eldh, "stddev", ELDHistogramStddev);
|
||||
env->SetProtoMethod(eldh, "percentile", ELDHistogramPercentile);
|
||||
env->SetProtoMethod(eldh, "percentiles", ELDHistogramPercentiles);
|
||||
env->SetProtoMethod(eldh, "enable", ELDHistogramEnable);
|
||||
env->SetProtoMethod(eldh, "disable", ELDHistogramDisable);
|
||||
env->SetProtoMethod(eldh, "reset", ELDHistogramReset);
|
||||
target->Set(context, eldh_classname,
|
||||
eldh->GetFunction(env->context()).ToLocalChecked()).FromJust();
|
||||
}
|
||||
|
||||
} // namespace performance
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "node_perf_common.h"
|
||||
#include "env.h"
|
||||
#include "base_object-inl.h"
|
||||
#include "histogram-inl.h"
|
||||
|
||||
#include "v8.h"
|
||||
#include "uv.h"
|
||||
@ -124,6 +125,41 @@ class GCPerformanceEntry : public PerformanceEntry {
|
||||
PerformanceGCKind gckind_;
|
||||
};
|
||||
|
||||
class ELDHistogram : public BaseObject, public Histogram {
|
||||
public:
|
||||
ELDHistogram(Environment* env,
|
||||
Local<Object> wrap,
|
||||
int32_t resolution);
|
||||
|
||||
~ELDHistogram() override;
|
||||
|
||||
bool RecordDelta();
|
||||
bool Enable();
|
||||
bool Disable();
|
||||
void ResetState() {
|
||||
Reset();
|
||||
exceeds_ = 0;
|
||||
prev_ = 0;
|
||||
}
|
||||
int64_t Exceeds() { return exceeds_; }
|
||||
|
||||
void MemoryInfo(MemoryTracker* tracker) const override {
|
||||
tracker->TrackFieldWithSize("histogram", GetMemorySize());
|
||||
}
|
||||
|
||||
SET_MEMORY_INFO_NAME(ELDHistogram)
|
||||
SET_SELF_SIZE(ELDHistogram)
|
||||
|
||||
private:
|
||||
void CloseTimer();
|
||||
|
||||
bool enabled_ = false;
|
||||
int32_t resolution_ = 0;
|
||||
int64_t exceeds_ = 0;
|
||||
uint64_t prev_ = 0;
|
||||
uv_timer_t* timer_;
|
||||
};
|
||||
|
||||
} // namespace performance
|
||||
} // namespace node
|
||||
|
||||
|
99
test/sequential/test-performance-eventloopdelay.js
Normal file
99
test/sequential/test-performance-eventloopdelay.js
Normal file
@ -0,0 +1,99 @@
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
const {
|
||||
monitorEventLoopDelay
|
||||
} = require('perf_hooks');
|
||||
|
||||
{
|
||||
const histogram = monitorEventLoopDelay();
|
||||
assert(histogram);
|
||||
assert(histogram.enable());
|
||||
assert(!histogram.enable());
|
||||
histogram.reset();
|
||||
assert(histogram.disable());
|
||||
assert(!histogram.disable());
|
||||
}
|
||||
|
||||
{
|
||||
[null, 'a', 1, false, Infinity].forEach((i) => {
|
||||
common.expectsError(
|
||||
() => monitorEventLoopDelay(i),
|
||||
{
|
||||
type: TypeError,
|
||||
code: 'ERR_INVALID_ARG_TYPE'
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
[null, 'a', false, {}, []].forEach((i) => {
|
||||
common.expectsError(
|
||||
() => monitorEventLoopDelay({ resolution: i }),
|
||||
{
|
||||
type: TypeError,
|
||||
code: 'ERR_INVALID_ARG_TYPE'
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
[-1, 0, Infinity].forEach((i) => {
|
||||
common.expectsError(
|
||||
() => monitorEventLoopDelay({ resolution: i }),
|
||||
{
|
||||
type: RangeError,
|
||||
code: 'ERR_INVALID_OPT_VALUE'
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
const histogram = monitorEventLoopDelay({ resolution: 1 });
|
||||
histogram.enable();
|
||||
let m = 5;
|
||||
function spinAWhile() {
|
||||
common.busyLoop(1000);
|
||||
if (--m > 0) {
|
||||
setTimeout(spinAWhile, common.platformTimeout(500));
|
||||
} else {
|
||||
histogram.disable();
|
||||
// The values are non-deterministic, so we just check that a value is
|
||||
// present, as opposed to a specific value.
|
||||
assert(histogram.min > 0);
|
||||
assert(histogram.max > 0);
|
||||
assert(histogram.stddev > 0);
|
||||
assert(histogram.mean > 0);
|
||||
assert(histogram.percentiles.size > 0);
|
||||
for (let n = 1; n < 100; n = n + 0.1) {
|
||||
assert(histogram.percentile(n) >= 0);
|
||||
}
|
||||
histogram.reset();
|
||||
assert.strictEqual(histogram.min, 9223372036854776000);
|
||||
assert.strictEqual(histogram.max, 0);
|
||||
assert(Number.isNaN(histogram.stddev));
|
||||
assert(Number.isNaN(histogram.mean));
|
||||
assert.strictEqual(histogram.percentiles.size, 1);
|
||||
|
||||
['a', false, {}, []].forEach((i) => {
|
||||
common.expectsError(
|
||||
() => histogram.percentile(i),
|
||||
{
|
||||
type: TypeError,
|
||||
code: 'ERR_INVALID_ARG_TYPE'
|
||||
}
|
||||
);
|
||||
});
|
||||
[-1, 0, 101].forEach((i) => {
|
||||
common.expectsError(
|
||||
() => histogram.percentile(i),
|
||||
{
|
||||
type: RangeError,
|
||||
code: 'ERR_INVALID_ARG_VALUE'
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
spinAWhile();
|
||||
}
|
@ -16,7 +16,7 @@ const jsPrimitives = {
|
||||
const jsGlobalObjectsUrl = `${jsDocPrefix}Reference/Global_Objects/`;
|
||||
const jsGlobalTypes = [
|
||||
'Array', 'ArrayBuffer', 'DataView', 'Date', 'Error', 'EvalError', 'Function',
|
||||
'Object', 'Promise', 'RangeError', 'ReferenceError', 'RegExp', 'Set',
|
||||
'Map', 'Object', 'Promise', 'RangeError', 'ReferenceError', 'RegExp', 'Set',
|
||||
'SharedArrayBuffer', 'SyntaxError', 'TypeError', 'TypedArray', 'URIError',
|
||||
'Uint8Array',
|
||||
];
|
||||
@ -75,6 +75,8 @@ const customTypesMap = {
|
||||
'fs.Stats': 'fs.html#fs_class_fs_stats',
|
||||
'fs.WriteStream': 'fs.html#fs_class_fs_writestream',
|
||||
|
||||
'Histogram': 'perf_hooks.html#perf_hooks_class_histogram',
|
||||
|
||||
'http.Agent': 'http.html#http_class_http_agent',
|
||||
'http.ClientRequest': 'http.html#http_class_http_clientrequest',
|
||||
'http.IncomingMessage': 'http.html#http_class_http_incomingmessage',
|
||||
|
@ -95,4 +95,6 @@ addlicense "large_pages" "src/large_pages" "$(sed -e '/SPDX-License-Identifier/,
|
||||
# brotli
|
||||
addlicense "brotli" "deps/brotli" "$(cat ${rootdir}/deps/brotli/LICENSE)"
|
||||
|
||||
addlicense "HdrHistogram" "deps/histogram" "$(cat ${rootdir}/deps/histogram/LICENSE.txt)"
|
||||
|
||||
mv $tmplicense $licensefile
|
||||
|
Loading…
Reference in New Issue
Block a user