@parca/profile 0.16.184 → 0.16.185
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/dist/Callgraph/constants.js +2 -2
- package/dist/Callgraph/index.js +35 -45
- package/dist/Callgraph/mockData/index.js +28 -11
- package/dist/Callgraph/utils.js +51 -58
- package/dist/GraphTooltip/ExpandOnHoverValue.js +2 -14
- package/dist/GraphTooltip/index.d.ts +5 -5
- package/dist/GraphTooltip/index.js +96 -122
- package/dist/MatchersInput/SuggestionItem.js +5 -17
- package/dist/MatchersInput/SuggestionsList.js +29 -53
- package/dist/MatchersInput/index.js +58 -74
- package/dist/MetricsCircle/index.js +2 -16
- package/dist/MetricsGraph/MetricsTooltip/index.js +27 -53
- package/dist/MetricsGraph/index.js +79 -98
- package/dist/MetricsSeries/index.js +4 -19
- package/dist/ProfileExplorer/ProfileExplorerCompare.js +4 -16
- package/dist/ProfileExplorer/ProfileExplorerSingle.js +2 -14
- package/dist/ProfileExplorer/index.js +129 -88
- package/dist/ProfileIcicleGraph/IcicleGraph/ColorStackLegend.js +15 -31
- package/dist/ProfileIcicleGraph/IcicleGraph/IcicleGraphNodes.d.ts +4 -4
- package/dist/ProfileIcicleGraph/IcicleGraph/IcicleGraphNodes.js +38 -54
- package/dist/ProfileIcicleGraph/IcicleGraph/index.d.ts +2 -2
- package/dist/ProfileIcicleGraph/IcicleGraph/index.js +15 -31
- package/dist/ProfileIcicleGraph/IcicleGraph/useColoredGraph.js +22 -26
- package/dist/ProfileIcicleGraph/IcicleGraph/useNodeColor.js +8 -9
- package/dist/ProfileIcicleGraph/IcicleGraph/utils.js +18 -20
- package/dist/ProfileIcicleGraph/index.d.ts +2 -2
- package/dist/ProfileIcicleGraph/index.js +18 -30
- package/dist/ProfileMetricsGraph/index.js +36 -88
- package/dist/ProfileSelector/CompareButton.js +8 -20
- package/dist/ProfileSelector/index.js +69 -69
- package/dist/ProfileSource.js +56 -65
- package/dist/ProfileTypeSelector/index.js +14 -28
- package/dist/ProfileView/FilterByFunctionButton.js +6 -7
- package/dist/ProfileView/ViewSelector.js +18 -31
- package/dist/ProfileView/VisualizationPanel.js +4 -16
- package/dist/ProfileView/index.js +72 -152
- package/dist/ProfileViewWithData.js +50 -101
- package/dist/TopTable/index.js +55 -63
- package/dist/components/DiffLegend.js +16 -28
- package/dist/components/ProfileShareButton/ResultBox.js +7 -21
- package/dist/components/ProfileShareButton/index.js +31 -90
- package/dist/useDelayedLoader.js +7 -8
- package/dist/useGrpcQuery/index.js +6 -48
- package/dist/useQuery.js +14 -58
- package/dist/utils.d.ts +1 -1
- package/dist/utils.js +16 -68
- package/package.json +6 -6
- package/src/Callgraph/index.tsx +3 -3
- package/src/Callgraph/utils.ts +1 -1
- package/src/GraphTooltip/index.tsx +17 -17
- package/src/MetricsGraph/index.tsx +3 -3
- package/src/ProfileIcicleGraph/IcicleGraph/IcicleGraphNodes.tsx +9 -10
- package/src/ProfileIcicleGraph/IcicleGraph/index.tsx +4 -8
- package/src/ProfileIcicleGraph/IcicleGraph/useNodeColor.ts +2 -2
- package/src/ProfileIcicleGraph/index.tsx +8 -8
- package/src/ProfileView/index.tsx +2 -2
- package/src/TopTable/index.tsx +3 -3
- package/src/utils.ts +2 -2
package/dist/useQuery.js
CHANGED
|
@@ -10,68 +10,24 @@
|
|
|
10
10
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
11
|
// See the License for the specific language governing permissions and
|
|
12
12
|
// limitations under the License.
|
|
13
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
14
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
15
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
16
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
17
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
18
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
19
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
20
|
-
});
|
|
21
|
-
};
|
|
22
|
-
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
23
|
-
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
24
|
-
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
25
|
-
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
26
|
-
function step(op) {
|
|
27
|
-
if (f) throw new TypeError("Generator is already executing.");
|
|
28
|
-
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
29
|
-
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
30
|
-
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
31
|
-
switch (op[0]) {
|
|
32
|
-
case 0: case 1: t = op; break;
|
|
33
|
-
case 4: _.label++; return { value: op[1], done: false };
|
|
34
|
-
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
35
|
-
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
36
|
-
default:
|
|
37
|
-
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
38
|
-
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
39
|
-
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
40
|
-
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
41
|
-
if (t[2]) _.ops.pop();
|
|
42
|
-
_.trys.pop(); continue;
|
|
43
|
-
}
|
|
44
|
-
op = body.call(thisArg, _);
|
|
45
|
-
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
46
|
-
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
47
|
-
}
|
|
48
|
-
};
|
|
49
13
|
import { useGrpcMetadata } from '@parca/components';
|
|
50
14
|
import useGrpcQuery from './useGrpcQuery';
|
|
51
|
-
export
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
key: ['query', profileSource, reportType, options
|
|
56
|
-
queryFn:
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
req.nodeTrimThreshold = options === null || options === void 0 ? void 0 : options.nodeTrimThreshold;
|
|
64
|
-
return [4 /*yield*/, client.query(req, { meta: metadata })];
|
|
65
|
-
case 1:
|
|
66
|
-
response = (_a.sent()).response;
|
|
67
|
-
return [2 /*return*/, response];
|
|
68
|
-
}
|
|
69
|
-
});
|
|
70
|
-
}); },
|
|
15
|
+
export const useQuery = (client, profileSource, reportType, options) => {
|
|
16
|
+
const { skip = false } = options ?? {};
|
|
17
|
+
const metadata = useGrpcMetadata();
|
|
18
|
+
const { data, isLoading, error } = useGrpcQuery({
|
|
19
|
+
key: ['query', profileSource, reportType, options?.nodeTrimThreshold],
|
|
20
|
+
queryFn: async () => {
|
|
21
|
+
const req = profileSource.QueryRequest();
|
|
22
|
+
req.reportType = reportType;
|
|
23
|
+
req.nodeTrimThreshold = options?.nodeTrimThreshold;
|
|
24
|
+
const { response } = await client.query(req, { meta: metadata });
|
|
25
|
+
return response;
|
|
26
|
+
},
|
|
71
27
|
options: {
|
|
72
28
|
enabled: !skip,
|
|
73
29
|
staleTime: 1000 * 60 * 5, // 5 minutes
|
|
74
30
|
},
|
|
75
|
-
})
|
|
76
|
-
return { isLoading
|
|
31
|
+
});
|
|
32
|
+
return { isLoading, error: error, response: data ?? null };
|
|
77
33
|
};
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { RpcMetadata } from '@protobuf-ts/runtime-rpc';
|
|
2
2
|
import { QueryRequest, QueryServiceClient } from '@parca/client';
|
|
3
|
-
export declare const hexifyAddress: (address?:
|
|
3
|
+
export declare const hexifyAddress: (address?: bigint) => string;
|
|
4
4
|
export declare const downloadPprof: (request: QueryRequest, queryClient: QueryServiceClient, metadata: RpcMetadata) => Promise<Blob>;
|
|
5
5
|
export declare const truncateString: (str: string, num: number) => string;
|
|
6
6
|
export declare const truncateStringReverse: (str: string, num: number) => string;
|
package/dist/utils.js
CHANGED
|
@@ -10,84 +10,32 @@
|
|
|
10
10
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
11
|
// See the License for the specific language governing permissions and
|
|
12
12
|
// limitations under the License.
|
|
13
|
-
var __assign = (this && this.__assign) || function () {
|
|
14
|
-
__assign = Object.assign || function(t) {
|
|
15
|
-
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
16
|
-
s = arguments[i];
|
|
17
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
18
|
-
t[p] = s[p];
|
|
19
|
-
}
|
|
20
|
-
return t;
|
|
21
|
-
};
|
|
22
|
-
return __assign.apply(this, arguments);
|
|
23
|
-
};
|
|
24
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
25
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
26
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
27
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
28
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
29
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
30
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
31
|
-
});
|
|
32
|
-
};
|
|
33
|
-
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
34
|
-
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
35
|
-
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
36
|
-
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
37
|
-
function step(op) {
|
|
38
|
-
if (f) throw new TypeError("Generator is already executing.");
|
|
39
|
-
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
40
|
-
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
41
|
-
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
42
|
-
switch (op[0]) {
|
|
43
|
-
case 0: case 1: t = op; break;
|
|
44
|
-
case 4: _.label++; return { value: op[1], done: false };
|
|
45
|
-
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
46
|
-
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
47
|
-
default:
|
|
48
|
-
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
49
|
-
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
50
|
-
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
51
|
-
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
52
|
-
if (t[2]) _.ops.pop();
|
|
53
|
-
_.trys.pop(); continue;
|
|
54
|
-
}
|
|
55
|
-
op = body.call(thisArg, _);
|
|
56
|
-
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
57
|
-
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
58
|
-
}
|
|
59
|
-
};
|
|
60
13
|
import { QueryRequest_ReportType } from '@parca/client';
|
|
61
|
-
export
|
|
14
|
+
export const hexifyAddress = (address) => {
|
|
62
15
|
if (address == null) {
|
|
63
16
|
return '';
|
|
64
17
|
}
|
|
65
|
-
return
|
|
18
|
+
return `0x${address.toString(16)}`;
|
|
19
|
+
};
|
|
20
|
+
export const downloadPprof = async (request, queryClient, metadata) => {
|
|
21
|
+
const req = {
|
|
22
|
+
...request,
|
|
23
|
+
reportType: QueryRequest_ReportType.PPROF,
|
|
24
|
+
};
|
|
25
|
+
const { response } = await queryClient.query(req, { meta: metadata });
|
|
26
|
+
if (response.report.oneofKind !== 'pprof') {
|
|
27
|
+
throw new Error(`Expected pprof report, got: ${response.report.oneofKind !== undefined ? response.report.oneofKind : 'undefined'}`);
|
|
28
|
+
}
|
|
29
|
+
const blob = new Blob([response.report.pprof], { type: 'application/octet-stream' });
|
|
30
|
+
return blob;
|
|
66
31
|
};
|
|
67
|
-
export
|
|
68
|
-
var req, response, blob;
|
|
69
|
-
return __generator(this, function (_a) {
|
|
70
|
-
switch (_a.label) {
|
|
71
|
-
case 0:
|
|
72
|
-
req = __assign(__assign({}, request), { reportType: QueryRequest_ReportType.PPROF });
|
|
73
|
-
return [4 /*yield*/, queryClient.query(req, { meta: metadata })];
|
|
74
|
-
case 1:
|
|
75
|
-
response = (_a.sent()).response;
|
|
76
|
-
if (response.report.oneofKind !== 'pprof') {
|
|
77
|
-
throw new Error("Expected pprof report, got: ".concat(response.report.oneofKind !== undefined ? response.report.oneofKind : 'undefined'));
|
|
78
|
-
}
|
|
79
|
-
blob = new Blob([response.report.pprof], { type: 'application/octet-stream' });
|
|
80
|
-
return [2 /*return*/, blob];
|
|
81
|
-
}
|
|
82
|
-
});
|
|
83
|
-
}); };
|
|
84
|
-
export var truncateString = function (str, num) {
|
|
32
|
+
export const truncateString = (str, num) => {
|
|
85
33
|
if (str.length <= num) {
|
|
86
34
|
return str;
|
|
87
35
|
}
|
|
88
36
|
return str.slice(0, num) + '...';
|
|
89
37
|
};
|
|
90
|
-
export
|
|
38
|
+
export const truncateStringReverse = (str, num) => {
|
|
91
39
|
if (str.length <= num) {
|
|
92
40
|
return str;
|
|
93
41
|
}
|
package/package.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@parca/profile",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.185",
|
|
4
4
|
"description": "Profile viewing libraries",
|
|
5
5
|
"dependencies": {
|
|
6
|
-
"@parca/client": "^0.16.
|
|
7
|
-
"@parca/components": "^0.16.
|
|
6
|
+
"@parca/client": "^0.16.74",
|
|
7
|
+
"@parca/components": "^0.16.146",
|
|
8
8
|
"@parca/dynamicsize": "^0.16.54",
|
|
9
9
|
"@parca/hooks": "^0.0.2",
|
|
10
10
|
"@parca/parser": "^0.16.55",
|
|
11
|
-
"@parca/store": "^0.16.
|
|
12
|
-
"@parca/utilities": "^0.0.
|
|
11
|
+
"@parca/store": "^0.16.78",
|
|
12
|
+
"@parca/utilities": "^0.0.8",
|
|
13
13
|
"@tanstack/react-query": "^4.0.5",
|
|
14
14
|
"@types/react-beautiful-dnd": "^13.1.3",
|
|
15
15
|
"d3": "7.8.5",
|
|
@@ -46,5 +46,5 @@
|
|
|
46
46
|
"access": "public",
|
|
47
47
|
"registry": "https://registry.npmjs.org/"
|
|
48
48
|
},
|
|
49
|
-
"gitHead": "
|
|
49
|
+
"gitHead": "e1dc36ad29a6efe06f45460bb721325df9416a6b"
|
|
50
50
|
}
|
package/src/Callgraph/index.tsx
CHANGED
|
@@ -64,7 +64,7 @@ const Callgraph = ({data, svgString, sampleUnit, width}: Props): JSX.Element =>
|
|
|
64
64
|
const maxColor: string = getNewSpanColor(isDarkMode);
|
|
65
65
|
const minColor: string = d3.scaleLinear([isDarkMode ? 'black' : 'white', maxColor])(0.3);
|
|
66
66
|
const colorRange: [string, string] = [minColor, maxColor];
|
|
67
|
-
const cumulatives = data.edges.map((edge: CallgraphEdge) =>
|
|
67
|
+
const cumulatives = data.edges.map((edge: CallgraphEdge) => edge.cumulative.toString());
|
|
68
68
|
const cumulativesRange = d3.extent(cumulatives);
|
|
69
69
|
const colorScale = d3
|
|
70
70
|
.scaleSequentialLog(d3.interpolateBlues)
|
|
@@ -147,8 +147,8 @@ const Callgraph = ({data, svgString, sampleUnit, width}: Props): JSX.Element =>
|
|
|
147
147
|
<GraphTooltip
|
|
148
148
|
type="callgraph"
|
|
149
149
|
unit={sampleUnit}
|
|
150
|
-
total={
|
|
151
|
-
totalUnfiltered={
|
|
150
|
+
total={data.cumulative}
|
|
151
|
+
totalUnfiltered={data.cumulative}
|
|
152
152
|
contextElement={containerRef.current}
|
|
153
153
|
/>
|
|
154
154
|
)}
|
package/src/Callgraph/utils.ts
CHANGED
|
@@ -117,7 +117,7 @@ export const jsonToDot = ({
|
|
|
117
117
|
|
|
118
118
|
const edgesAsStrings = edges.map((edge: CallgraphEdge) => {
|
|
119
119
|
const dataAttributes = {
|
|
120
|
-
cumulative: edge.cumulative,
|
|
120
|
+
cumulative: Number(edge.cumulative),
|
|
121
121
|
color: withAlphaHex(colorRange[1], colorOpacityScale(Number(edge.cumulative))),
|
|
122
122
|
className: 'edge',
|
|
123
123
|
// boxHeight: DEFAULT_NODE_HEIGHT,
|
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
} from '@parca/client/dist/parca/metastore/v1alpha1/metastore';
|
|
32
32
|
import {useKeyDown} from '@parca/components';
|
|
33
33
|
import {selectHoveringNode, useAppSelector} from '@parca/store';
|
|
34
|
-
import {getLastItem, valueFormatter} from '@parca/utilities';
|
|
34
|
+
import {divide, getLastItem, valueFormatter} from '@parca/utilities';
|
|
35
35
|
|
|
36
36
|
import {hexifyAddress, truncateString, truncateStringReverse} from '../';
|
|
37
37
|
import {ExpandOnHover} from './ExpandOnHoverValue';
|
|
@@ -46,7 +46,7 @@ interface ExtendedCallgraphNodeMeta extends CallgraphNodeMeta {
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
interface HoveringNode extends FlamegraphRootNode, FlamegraphNode, CallgraphNode {
|
|
49
|
-
diff:
|
|
49
|
+
diff: bigint;
|
|
50
50
|
meta?: FlamegraphNodeMeta | ExtendedCallgraphNodeMeta;
|
|
51
51
|
}
|
|
52
52
|
|
|
@@ -54,8 +54,8 @@ interface GraphTooltipProps {
|
|
|
54
54
|
x?: number;
|
|
55
55
|
y?: number;
|
|
56
56
|
unit: string;
|
|
57
|
-
total:
|
|
58
|
-
totalUnfiltered:
|
|
57
|
+
total: bigint;
|
|
58
|
+
totalUnfiltered: bigint;
|
|
59
59
|
hoveringNode?: HoveringNode;
|
|
60
60
|
contextElement: Element | null;
|
|
61
61
|
isFixed?: boolean;
|
|
@@ -158,11 +158,11 @@ const TooltipMetaInfo = ({
|
|
|
158
158
|
if (hoveringNode.meta?.function == null) return '<unknown>';
|
|
159
159
|
|
|
160
160
|
return `${hoveringNode.meta.function.filename} ${
|
|
161
|
-
hoveringNode.meta.line?.line !== undefined && hoveringNode.meta.line?.line !==
|
|
161
|
+
hoveringNode.meta.line?.line !== undefined && hoveringNode.meta.line?.line !== 0n
|
|
162
162
|
? ` +${hoveringNode.meta.line.line.toString()}`
|
|
163
163
|
: `${
|
|
164
164
|
hoveringNode.meta.function?.startLine !== undefined &&
|
|
165
|
-
hoveringNode.meta.function?.startLine !==
|
|
165
|
+
hoveringNode.meta.function?.startLine !== 0n
|
|
166
166
|
? ` +${hoveringNode.meta.function.startLine}`
|
|
167
167
|
: ''
|
|
168
168
|
}`
|
|
@@ -192,7 +192,7 @@ const TooltipMetaInfo = ({
|
|
|
192
192
|
<td className="w-1/4">Address</td>
|
|
193
193
|
<td className="w-3/4 break-all">
|
|
194
194
|
{hoveringNode.meta?.location?.address == null ||
|
|
195
|
-
hoveringNode.meta?.location.address ===
|
|
195
|
+
hoveringNode.meta?.location.address === 0n ? (
|
|
196
196
|
<NoData />
|
|
197
197
|
) : (
|
|
198
198
|
<CopyToClipboard
|
|
@@ -255,8 +255,8 @@ export const GraphTooltipContent = ({
|
|
|
255
255
|
}: {
|
|
256
256
|
hoveringNode: HoveringNode;
|
|
257
257
|
unit: string;
|
|
258
|
-
total:
|
|
259
|
-
totalUnfiltered:
|
|
258
|
+
total: bigint;
|
|
259
|
+
totalUnfiltered: bigint;
|
|
260
260
|
isFixed: boolean;
|
|
261
261
|
strings?: string[];
|
|
262
262
|
mappings?: Mapping[];
|
|
@@ -275,22 +275,22 @@ export const GraphTooltipContent = ({
|
|
|
275
275
|
timeoutHandle = setTimeout(() => setIsCopied(false), 3000);
|
|
276
276
|
};
|
|
277
277
|
|
|
278
|
-
const hoveringNodeCumulative =
|
|
279
|
-
const diff = hoveringNode.diff
|
|
278
|
+
const hoveringNodeCumulative = hoveringNode.cumulative;
|
|
279
|
+
const diff = hoveringNode.diff;
|
|
280
280
|
const prevValue = hoveringNodeCumulative - diff;
|
|
281
|
-
const diffRatio =
|
|
281
|
+
const diffRatio = diff !== 0n ? divide(diff, prevValue) : 0;
|
|
282
282
|
const diffSign = diff > 0 ? '+' : '';
|
|
283
283
|
const diffValueText = diffSign + valueFormatter(diff, unit, 1);
|
|
284
284
|
const diffPercentageText = diffSign + (diffRatio * 100).toFixed(2) + '%';
|
|
285
285
|
const diffText = `${diffValueText} (${diffPercentageText})`;
|
|
286
286
|
|
|
287
|
-
const getTextForCumulative = (hoveringNodeCumulative:
|
|
287
|
+
const getTextForCumulative = (hoveringNodeCumulative: bigint): string => {
|
|
288
288
|
const filtered =
|
|
289
289
|
totalUnfiltered > total
|
|
290
|
-
? ` / ${(
|
|
290
|
+
? ` / ${divide(hoveringNodeCumulative * 100n, total).toFixed(2)}% of filtered`
|
|
291
291
|
: '';
|
|
292
292
|
return `${valueFormatter(hoveringNodeCumulative, unit, 2)}
|
|
293
|
-
(${(
|
|
293
|
+
(${divide(hoveringNodeCumulative * 100n, totalUnfiltered).toFixed(2)}%${filtered})`;
|
|
294
294
|
};
|
|
295
295
|
|
|
296
296
|
return (
|
|
@@ -314,7 +314,7 @@ export const GraphTooltipContent = ({
|
|
|
314
314
|
) : (
|
|
315
315
|
<>
|
|
316
316
|
{hoveringNode.meta.location !== undefined &&
|
|
317
|
-
|
|
317
|
+
hoveringNode.meta.location.address !== 0n ? (
|
|
318
318
|
<CopyToClipboard
|
|
319
319
|
onCopy={onCopy}
|
|
320
320
|
text={hexifyAddress(hoveringNode.meta.location.address)}
|
|
@@ -347,7 +347,7 @@ export const GraphTooltipContent = ({
|
|
|
347
347
|
</CopyToClipboard>
|
|
348
348
|
</td>
|
|
349
349
|
</tr>
|
|
350
|
-
{hoveringNode.diff !== undefined && diff !==
|
|
350
|
+
{hoveringNode.diff !== undefined && diff !== 0n && (
|
|
351
351
|
<tr>
|
|
352
352
|
<td className="w-1/4">Diff</td>
|
|
353
353
|
<td className="w-3/4">
|
|
@@ -139,8 +139,8 @@ export const RawMetricsGraph = ({
|
|
|
139
139
|
metric,
|
|
140
140
|
values: s.samples.reduce<number[][]>(function (agg: number[][], d: MetricsSample) {
|
|
141
141
|
if (d.timestamp !== undefined && d.valuePerSecond !== undefined) {
|
|
142
|
-
const t = (
|
|
143
|
-
agg.push([t, d.valuePerSecond,
|
|
142
|
+
const t = (Number(d.timestamp.seconds) * 1e9 + d.timestamp.nanos) / 1e6; // https://github.com/microsoft/TypeScript/issues/5710#issuecomment-157886246
|
|
143
|
+
agg.push([t, d.valuePerSecond, Number(d.value), Number(d.duration)]);
|
|
144
144
|
}
|
|
145
145
|
return agg;
|
|
146
146
|
}, []),
|
|
@@ -474,7 +474,7 @@ export const RawMetricsGraph = ({
|
|
|
474
474
|
>
|
|
475
475
|
<line stroke="currentColor" x2={-6} />
|
|
476
476
|
<text fill="currentColor" x={-9} dy={'0.32em'}>
|
|
477
|
-
{valueFormatter(
|
|
477
|
+
{valueFormatter(1000, sampleUnit, 1)}
|
|
478
478
|
</text>
|
|
479
479
|
</g>
|
|
480
480
|
))}
|
|
@@ -14,7 +14,6 @@
|
|
|
14
14
|
import React, {useMemo} from 'react';
|
|
15
15
|
|
|
16
16
|
import cx from 'classnames';
|
|
17
|
-
import {scaleLinear} from 'd3-scale';
|
|
18
17
|
|
|
19
18
|
import {FlamegraphNode} from '@parca/client';
|
|
20
19
|
import {
|
|
@@ -24,7 +23,7 @@ import {
|
|
|
24
23
|
} from '@parca/client/dist/parca/metastore/v1alpha1/metastore';
|
|
25
24
|
import {useKeyDown} from '@parca/components';
|
|
26
25
|
import {selectBinaries, setHoveringNode, useAppDispatch, useAppSelector} from '@parca/store';
|
|
27
|
-
import {isSearchMatch} from '@parca/utilities';
|
|
26
|
+
import {isSearchMatch, scaleLinear} from '@parca/utilities';
|
|
28
27
|
|
|
29
28
|
import useNodeColor from './useNodeColor';
|
|
30
29
|
import {nodeLabel} from './utils';
|
|
@@ -39,13 +38,13 @@ interface IcicleGraphNodesProps {
|
|
|
39
38
|
functions: ParcaFunction[];
|
|
40
39
|
x: number;
|
|
41
40
|
y: number;
|
|
42
|
-
total:
|
|
41
|
+
total: bigint;
|
|
43
42
|
totalWidth: number;
|
|
44
43
|
level: number;
|
|
45
44
|
curPath: string[];
|
|
46
45
|
setCurPath: (path: string[]) => void;
|
|
47
46
|
path: string[];
|
|
48
|
-
xScale: (value:
|
|
47
|
+
xScale: (value: bigint) => number;
|
|
49
48
|
searchString?: string;
|
|
50
49
|
compareMode: boolean;
|
|
51
50
|
}
|
|
@@ -82,7 +81,7 @@ export const IcicleGraphNodes = React.memo(function IcicleGraphNodesNoMemo({
|
|
|
82
81
|
return (
|
|
83
82
|
<g transform={`translate(${x}, ${y})`}>
|
|
84
83
|
{nodes.map(function nodeMapper(d, i) {
|
|
85
|
-
const start = nodes.slice(0, i).reduce((sum, d) => sum +
|
|
84
|
+
const start = nodes.slice(0, i).reduce((sum, d) => sum + d.cumulative, 0n);
|
|
86
85
|
const xStart = xScale(start);
|
|
87
86
|
|
|
88
87
|
return (
|
|
@@ -125,9 +124,9 @@ interface IcicleNodeProps {
|
|
|
125
124
|
locations: Location[];
|
|
126
125
|
functions: ParcaFunction[];
|
|
127
126
|
path: string[];
|
|
128
|
-
total:
|
|
127
|
+
total: bigint;
|
|
129
128
|
setCurPath: (path: string[]) => void;
|
|
130
|
-
xScale: (value:
|
|
129
|
+
xScale: (value: bigint) => number;
|
|
131
130
|
isRoot?: boolean;
|
|
132
131
|
searchString?: string;
|
|
133
132
|
compareMode: boolean;
|
|
@@ -176,11 +175,11 @@ export const IcicleNode = React.memo(function IcicleNodeNoMemo({
|
|
|
176
175
|
const isFaded = curPath.length > 0 && name !== curPath[curPath.length - 1];
|
|
177
176
|
const styles = isFaded ? fadedIcicleRectStyles : icicleRectStyles;
|
|
178
177
|
const nextLevel = level + 1;
|
|
179
|
-
const cumulative =
|
|
178
|
+
const cumulative = data.cumulative;
|
|
180
179
|
const nextCurPath = curPath.length === 0 ? [] : curPath.slice(1);
|
|
181
180
|
const newXScale =
|
|
182
181
|
nextCurPath.length === 0 && curPath.length === 1
|
|
183
|
-
? scaleLinear(
|
|
182
|
+
? scaleLinear([0n, cumulative], [0, totalWidth])
|
|
184
183
|
: xScale;
|
|
185
184
|
|
|
186
185
|
const width =
|
|
@@ -203,7 +202,7 @@ export const IcicleNode = React.memo(function IcicleNodeNoMemo({
|
|
|
203
202
|
if (isShiftDown) return;
|
|
204
203
|
|
|
205
204
|
// need to add id and flat for tooltip purposes
|
|
206
|
-
dispatch(setHoveringNode({...data, id: '', flat:
|
|
205
|
+
dispatch(setHoveringNode({...data, id: '', flat: 0n}));
|
|
207
206
|
};
|
|
208
207
|
const onMouseLeave = (): void => {
|
|
209
208
|
if (isShiftDown) return;
|
|
@@ -13,11 +13,9 @@
|
|
|
13
13
|
|
|
14
14
|
import {memo, useEffect, useMemo, useRef, useState} from 'react';
|
|
15
15
|
|
|
16
|
-
import {scaleLinear} from 'd3-scale';
|
|
17
|
-
|
|
18
16
|
import {Flamegraph} from '@parca/client';
|
|
19
17
|
import {setHoveringNode, useAppDispatch} from '@parca/store';
|
|
20
|
-
import {selectQueryParam, type NavigateFunction} from '@parca/utilities';
|
|
18
|
+
import {scaleLinear, selectQueryParam, type NavigateFunction} from '@parca/utilities';
|
|
21
19
|
|
|
22
20
|
import GraphTooltip from '../../GraphTooltip';
|
|
23
21
|
import ColorStackLegend from './ColorStackLegend';
|
|
@@ -26,8 +24,8 @@ import useColoredGraph from './useColoredGraph';
|
|
|
26
24
|
|
|
27
25
|
interface IcicleGraphProps {
|
|
28
26
|
graph: Flamegraph;
|
|
29
|
-
total:
|
|
30
|
-
filtered:
|
|
27
|
+
total: bigint;
|
|
28
|
+
filtered: bigint;
|
|
31
29
|
sampleUnit: string;
|
|
32
30
|
width?: number;
|
|
33
31
|
curPath: string[];
|
|
@@ -65,9 +63,7 @@ export const IcicleGraph = memo(function IcicleGraph({
|
|
|
65
63
|
if (width === undefined) {
|
|
66
64
|
return () => 0;
|
|
67
65
|
}
|
|
68
|
-
return scaleLinear()
|
|
69
|
-
.domain([0, Number(total)])
|
|
70
|
-
.range([0, width]);
|
|
66
|
+
return scaleLinear([0n, total], [0, width]);
|
|
71
67
|
}, [total, width]);
|
|
72
68
|
|
|
73
69
|
if (coloredGraph.root === undefined || width === undefined) {
|
|
@@ -29,8 +29,8 @@ const useNodeColor = ({data, compareMode}: Props): string => {
|
|
|
29
29
|
|
|
30
30
|
const color: string = useMemo(() => {
|
|
31
31
|
if (compareMode) {
|
|
32
|
-
const diff =
|
|
33
|
-
const cumulative =
|
|
32
|
+
const diff = data.diff;
|
|
33
|
+
const cumulative = data.cumulative;
|
|
34
34
|
return diffColor(diff, cumulative, isDarkMode);
|
|
35
35
|
}
|
|
36
36
|
|
|
@@ -16,7 +16,7 @@ import {useEffect, useMemo} from 'react';
|
|
|
16
16
|
import {Flamegraph} from '@parca/client';
|
|
17
17
|
import {Button} from '@parca/components';
|
|
18
18
|
import {useContainerDimensions} from '@parca/hooks';
|
|
19
|
-
import {selectQueryParam, type NavigateFunction} from '@parca/utilities';
|
|
19
|
+
import {divide, selectQueryParam, type NavigateFunction} from '@parca/utilities';
|
|
20
20
|
|
|
21
21
|
import DiffLegend from '../components/DiffLegend';
|
|
22
22
|
import {IcicleGraph} from './IcicleGraph';
|
|
@@ -28,8 +28,8 @@ export type ResizeHandler = (width: number, height: number) => void;
|
|
|
28
28
|
interface ProfileIcicleGraphProps {
|
|
29
29
|
width?: number;
|
|
30
30
|
graph: Flamegraph | undefined;
|
|
31
|
-
total:
|
|
32
|
-
filtered:
|
|
31
|
+
total: bigint;
|
|
32
|
+
filtered: bigint;
|
|
33
33
|
sampleUnit: string;
|
|
34
34
|
curPath: string[] | [];
|
|
35
35
|
setNewCurPath: (path: string[]) => void;
|
|
@@ -75,20 +75,20 @@ const ProfileIcicleGraph = ({
|
|
|
75
75
|
return ['0', '0', false, '0', '0', false, '0', '0'];
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
const trimmed =
|
|
78
|
+
const trimmed = graph.trimmed;
|
|
79
79
|
|
|
80
80
|
const totalUnfiltered = total + filtered;
|
|
81
81
|
// safeguard against division by zero
|
|
82
|
-
const totalUnfilteredDivisor = totalUnfiltered > 0 ? totalUnfiltered :
|
|
82
|
+
const totalUnfilteredDivisor = totalUnfiltered > 0 ? totalUnfiltered : 1n;
|
|
83
83
|
|
|
84
84
|
return [
|
|
85
85
|
numberFormatter.format(total),
|
|
86
86
|
numberFormatter.format(totalUnfiltered),
|
|
87
87
|
trimmed > 0,
|
|
88
88
|
numberFormatter.format(trimmed),
|
|
89
|
-
numberFormatter.format((trimmed *
|
|
89
|
+
numberFormatter.format(divide(trimmed * 100n, totalUnfilteredDivisor)),
|
|
90
90
|
filtered > 0,
|
|
91
|
-
numberFormatter.format((total *
|
|
91
|
+
numberFormatter.format(divide(total * 100n, totalUnfilteredDivisor)),
|
|
92
92
|
];
|
|
93
93
|
}, [graph, filtered, total]);
|
|
94
94
|
|
|
@@ -112,7 +112,7 @@ const ProfileIcicleGraph = ({
|
|
|
112
112
|
|
|
113
113
|
if (graph === undefined) return <div>no data...</div>;
|
|
114
114
|
|
|
115
|
-
if (total ===
|
|
115
|
+
if (total === 0n && !loading) return <>Profile has no samples</>;
|
|
116
116
|
|
|
117
117
|
if (isTrimmed) {
|
|
118
118
|
console.info(`Trimmed ${trimmedFormatted} (${trimmedPercentage}%) too small values.`);
|
|
@@ -245,8 +245,8 @@ export const ProfileView = ({
|
|
|
245
245
|
curPath={curPath}
|
|
246
246
|
setNewCurPath={setNewCurPath}
|
|
247
247
|
graph={flamegraphData.data}
|
|
248
|
-
total={
|
|
249
|
-
filtered={
|
|
248
|
+
total={total}
|
|
249
|
+
filtered={filtered}
|
|
250
250
|
sampleUnit={sampleUnit}
|
|
251
251
|
onContainerResize={onFlamegraphContainerResize}
|
|
252
252
|
navigateTo={navigateTo}
|
package/src/TopTable/index.tsx
CHANGED
|
@@ -100,7 +100,7 @@ export const TopTable = React.memo(function TopTable({
|
|
|
100
100
|
}),
|
|
101
101
|
columnHelper.accessor('flat', {
|
|
102
102
|
header: () => 'Flat',
|
|
103
|
-
cell: info => valueFormatter(
|
|
103
|
+
cell: info => valueFormatter(info.getValue(), unit, 2),
|
|
104
104
|
size: 150,
|
|
105
105
|
meta: {
|
|
106
106
|
align: 'right',
|
|
@@ -109,7 +109,7 @@ export const TopTable = React.memo(function TopTable({
|
|
|
109
109
|
}),
|
|
110
110
|
columnHelper.accessor('cumulative', {
|
|
111
111
|
header: () => 'Cumulative',
|
|
112
|
-
cell: info => valueFormatter(
|
|
112
|
+
cell: info => valueFormatter(info.getValue(), unit, 2),
|
|
113
113
|
size: 150,
|
|
114
114
|
meta: {
|
|
115
115
|
align: 'right',
|
|
@@ -121,7 +121,7 @@ export const TopTable = React.memo(function TopTable({
|
|
|
121
121
|
cols.push(
|
|
122
122
|
columnHelper.accessor('diff', {
|
|
123
123
|
header: () => 'Diff',
|
|
124
|
-
cell: info => addPlusSign(valueFormatter(
|
|
124
|
+
cell: info => addPlusSign(valueFormatter(info.getValue(), unit, 2)),
|
|
125
125
|
size: 150,
|
|
126
126
|
meta: {
|
|
127
127
|
align: 'right',
|
package/src/utils.ts
CHANGED
|
@@ -15,11 +15,11 @@ import type {RpcMetadata} from '@protobuf-ts/runtime-rpc';
|
|
|
15
15
|
|
|
16
16
|
import {QueryRequest, QueryRequest_ReportType, QueryServiceClient} from '@parca/client';
|
|
17
17
|
|
|
18
|
-
export const hexifyAddress = (address?:
|
|
18
|
+
export const hexifyAddress = (address?: bigint): string => {
|
|
19
19
|
if (address == null) {
|
|
20
20
|
return '';
|
|
21
21
|
}
|
|
22
|
-
return `0x${
|
|
22
|
+
return `0x${address.toString(16)}`;
|
|
23
23
|
};
|
|
24
24
|
|
|
25
25
|
export const downloadPprof = async (
|