Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(webapp): Make explore page show precise numbers in table #1695

Merged
merged 4 commits into from
Nov 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 61 additions & 1 deletion packages/pyroscope-flamegraph/src/format/format.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,16 @@ describe('format', () => {
});
});

describe('format', () => {
describe('format & formatPrecise', () => {
// TODO is this correct, since we have an enum?
// unfortunately until we fully migrate to TS
// we still need to check for a default value
it('its constructor should default to DurationFormatter', () => {
const df = getFormatter(80, 2, '' as any);

expect(df.format(0.001, 100)).toBe('< 0.01 ');
expect(df.formatPrecise(0.001, 100)).toBe('0.00001 ');
expect(df.formatPrecise(0.1, 100)).toBe('0.001 ');
});

describe('DurationFormatter', () => {
Expand All @@ -35,6 +37,7 @@ describe('format', () => {
expect(df.format(2000, 100)).toBe('20.00 seconds');
expect(df.format(2012.3, 100)).toBe('20.12 seconds');
expect(df.format(8000, 100)).toBe('80.00 seconds');
expect(df.formatPrecise(0.001, 100)).toBe('0.00001 seconds');
});

it('correctly formats duration when maxdur = 80', () => {
Expand All @@ -45,6 +48,7 @@ describe('format', () => {
expect(df.format(2000, 100)).toBe('0.33 minutes');
expect(df.format(2012.3, 100)).toBe('0.34 minutes');
expect(df.format(8000, 100)).toBe('1.33 minutes');
expect(df.formatPrecise(1, 100)).toBe('0.00017 minutes');
});

it('correctly formats samples duration and return value without units', () => {
Expand All @@ -66,6 +70,7 @@ describe('format', () => {
expect(df.format(2000, 100)).toBe('20.00 seconds');
expect(df.format(2012.3, 100)).toBe('20.12 seconds');
expect(df.format(8000, 100)).toBe('80.00 seconds');
expect(df.formatPrecise(0.001, 100)).toBe('0.00001 seconds');
});

it('correctly formats trace_samples duration when maxdur is less than second', () => {
Expand All @@ -77,6 +82,7 @@ describe('format', () => {
expect(df.format(9999, 100)).toBe('99990.00 ms');
expect(df.format(0.331, 100)).toBe('3.31 ms');
expect(df.format(0.0001, 100)).toBe('< 0.01 ms');
expect(df.formatPrecise(0.0001, 100)).toBe('0.001 ms');
});

it('correctly formats trace_samples duration when maxdur is less than ms', () => {
Expand All @@ -87,6 +93,8 @@ describe('format', () => {
expect(df.format(0.0091, 100)).toBe('91.00 μs');
expect(df.format(1.005199, 100)).toBe('10051.99 μs');
expect(df.format(1.1, 100)).toBe('11000.00 μs');
expect(df.format(0.000001, 100)).toBe('< 0.01 μs');
expect(df.formatPrecise(0.0000001, 100)).toBe('0.001 μs');
});

it('correctly formats trace_samples duration when maxdur is hour', () => {
Expand All @@ -99,6 +107,7 @@ describe('format', () => {
expect(df.format(0.02 * hour * 100, 100)).toBe('0.02 hours');
expect(df.format(0.001 * hour * 100, 100)).toBe('< 0.01 hours');
expect(df.format(42.1 * hour * 100, 100)).toBe('42.10 hours');
expect(df.formatPrecise(0.001 * hour * 100, 100)).toBe('0.001 hours');
});

it('correctly formats trace_samples duration when maxdur is day', () => {
Expand All @@ -110,6 +119,7 @@ describe('format', () => {
expect(df.format(2.29 * day * 100, 100)).toBe('2.29 days');
expect(df.format(0.11 * day * 100, 100)).toBe('0.11 days');
expect(df.format(0.001 * day * 100, 100)).toBe('< 0.01 days');
expect(df.formatPrecise(0.001 * day * 100, 100)).toBe('0.001 days');
});

it('correctly formats trace_samples duration when maxdur = month', () => {
Expand All @@ -121,6 +131,7 @@ describe('format', () => {
expect(df.format(5.142 * month * 100, 100)).toBe('5.14 months');
expect(df.format(0.88 * month * 100, 100)).toBe('0.88 months');
expect(df.format(0.008 * month * 100, 100)).toBe('< 0.01 months');
expect(df.formatPrecise(0.008 * month * 100, 100)).toBe('0.008 months');
});

it('correctly formats trace_samples duration when maxdur = year', () => {
Expand All @@ -132,6 +143,7 @@ describe('format', () => {
expect(df.format(3.414 * year * 100, 100)).toBe('3.41 years');
expect(df.format(0.12 * year * 100, 100)).toBe('0.12 years');
expect(df.format(0.008 * year * 100, 100)).toBe('< 0.01 years');
expect(df.formatPrecise(0.008 * year * 100, 100)).toBe('0.008 years');
});
});

Expand Down Expand Up @@ -194,6 +206,27 @@ describe('format', () => {
);
});

describe('ObjectsFormatter formatPrecise', () => {
describe.each([
[1, -1, '-1 '],
[100_000, -1, '-0.001 K'],

[1, 1, '1 '],
[100_000, 1, '0.001 K'],
])(
'new ObjectsFormatter(%i).format(%i, %i)',
(maxObjects: number, samples: number, expected: string) => {
it(`returns ${expected}`, () => {
// sampleRate is not used
const sampleRate = NaN;
const f = getFormatter(maxObjects, sampleRate, 'objects');

expect(f.formatPrecise(samples, sampleRate)).toBe(expected);
});
}
);
});

describe('BytesFormatter', () => {
describe.each([
[1, -1, '-1.00 bytes'], // TODO is this correct?
Expand Down Expand Up @@ -227,5 +260,32 @@ describe('format', () => {
}
);
});

describe('BytesFormatter', () => {
describe.each([
[1, -1, '-1 bytes'],
[1024, -1, '-0.00098 KB'],
[1024 ** 2, -10, '-0.00001 MB'],
[1024 ** 3, -10000, '-0.00001 GB'],
[1024 ** 4, -10000000, '-0.00001 TB'],

[1, 1, '1 bytes'],
[1024, 1, '0.00098 KB'],
[1024 ** 2, 10, '0.00001 MB'],
[1024 ** 3, 10000, '0.00001 GB'],
[1024 ** 4, 10000000, '0.00001 TB'],
])(
'new BytesFormatter(%i).format(%i, %i)',
(maxObjects: number, samples: number, expected: string) => {
it(`returns ${expected}`, () => {
// sampleRate is not used
const sampleRate = NaN;
const f = getFormatter(maxObjects, sampleRate, 'bytes');

expect(f.formatPrecise(samples, sampleRate)).toBe(expected);
});
}
);
});
});
});
30 changes: 30 additions & 0 deletions packages/pyroscope-flamegraph/src/format/format.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,18 @@ class DurationFormatter {
}`
: nStr;
}

formatPrecise(samples: number, sampleRate: number) {
if (this.enableSubsecondPrecision) {
sampleRate /= 1e6;
}
const n = samples / sampleRate / this.divider;

return `${parseFloat(n.toFixed(5))} ${
this.units ||
`${this.suffix}${n === 1 || this.suffix.length === 2 ? '' : 's'}`
}`;
}
}

// this is a class and not a function because we can save some time by
Expand Down Expand Up @@ -160,6 +172,12 @@ class NanosecondsFormatter {

return `${nStr} ${this.suffix}${n === 1 ? '' : 's'}`;
}

formatPrecise(samples: number) {
const n = samples / 1000000000 / this.divider;

return `${parseFloat(n.toFixed(5))} ${this.suffix}${n === 1 ? '' : 's'}`;
}
}

export class ObjectsFormatter {
Expand Down Expand Up @@ -208,6 +226,12 @@ export class ObjectsFormatter {
}
return `${nStr} ${this.suffix}`;
}

formatPrecise(samples: number) {
const n = samples / this.divider;

return `${parseFloat(n.toFixed(5))} ${this.suffix}`;
}
}

export class BytesFormatter {
Expand Down Expand Up @@ -262,4 +286,10 @@ export class BytesFormatter {

return `${nStr} ${this.suffix}`;
}

formatPrecise(samples: number) {
const n = samples / this.divider;

return `${parseFloat(n.toFixed(5))} ${this.suffix}`;
}
}
4 changes: 3 additions & 1 deletion webapp/javascript/pages/TagExplorerView.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ $pieChartWidth: 300px;

.tagExplorerTable {
width: 100%;
font-variant-numeric: lining-nums tabular-nums;

thead,
tbody tr:nth-child(2n):not(:hover) {
Expand All @@ -79,7 +80,8 @@ $pieChartWidth: 300px;

td {
font-weight: initial;
text-align: center;
text-align: left;
font-family: monospace;
}

tr.activeTagRow {
Expand Down
Loading