refactor: update terminology and improve messaging for performance profiling components

This commit is contained in:
Nawaz Dhandala
2026-04-02 12:37:03 +01:00
parent 23da31b50c
commit e15a934b3f
14 changed files with 159 additions and 78 deletions

View File

@@ -125,8 +125,8 @@ const DashboardNavbar: FunctionComponent<ComponentProps> = (
category: "Observability",
},
{
title: "Profiles",
description: "CPU and memory profiling.",
title: "Performance Profiles",
description: "Find slow functions and memory hotspots.",
route: RouteUtil.populateRouteParams(RouteMap[PageMap.PROFILES] as Route),
activeRoute: RouteMap[PageMap.PROFILES],
icon: IconProp.Fire,

View File

@@ -184,7 +184,7 @@ const DiffFlamegraph: FunctionComponent<DiffFlamegraphProps> = (
) {
return (
<div className="p-8 text-center text-gray-500">
No profile data found in the selected time ranges.
No performance data found in the selected time ranges. Try adjusting the time periods.
</div>
);
}
@@ -323,14 +323,14 @@ const DiffFlamegraph: FunctionComponent<DiffFlamegraphProps> = (
)}
<div className="mb-3 flex flex-wrap items-center space-x-4 text-xs text-gray-600">
<span className="font-medium">Legend:</span>
<span className="font-medium">What the colors mean:</span>
<span className="flex items-center space-x-1">
<span className="inline-block w-3 h-3 rounded bg-red-500" />
<span>Regression (slower)</span>
<span>Got slower</span>
</span>
<span className="flex items-center space-x-1">
<span className="inline-block w-3 h-3 rounded bg-green-500" />
<span>Improvement (faster)</span>
<span>Got faster</span>
</span>
<span className="flex items-center space-x-1">
<span className="inline-block w-3 h-3 rounded bg-gray-400" />
@@ -364,9 +364,9 @@ const DiffFlamegraph: FunctionComponent<DiffFlamegraphProps> = (
<div className="text-gray-300">{tooltip.fileName}</div>
)}
<div className="mt-1">
Baseline: {tooltip.baselineValue.toLocaleString()}
Before: {tooltip.baselineValue.toLocaleString()}
</div>
<div>Comparison: {tooltip.comparisonValue.toLocaleString()}</div>
<div>After: {tooltip.comparisonValue.toLocaleString()}</div>
<div
className={
tooltip.delta > 0
@@ -376,7 +376,7 @@ const DiffFlamegraph: FunctionComponent<DiffFlamegraphProps> = (
: ""
}
>
Delta: {tooltip.delta > 0 ? "+" : ""}
Change: {tooltip.delta > 0 ? "+" : ""}
{tooltip.delta.toLocaleString()} (
{tooltip.deltaPercent >= 0 ? "+" : ""}
{tooltip.deltaPercent.toFixed(1)}%)

View File

@@ -211,7 +211,7 @@ const ProfileFlamegraph: FunctionComponent<ProfileFlamegraphProps> = (
if (samples.length === 0) {
return (
<div className="p-8 text-center text-gray-500">
No profile samples found for this profile.
No performance data found for this profile. This can happen if the profile was recently captured and data is still being processed.
</div>
);
}
@@ -325,19 +325,25 @@ const ProfileFlamegraph: FunctionComponent<ProfileFlamegraphProps> = (
)}
<div className="mb-3 flex flex-wrap items-center space-x-4 text-xs text-gray-600">
<span className="font-medium">Frame Types:</span>
{["kernel", "native", "jvm", "cpython", "go", "v8js", "unknown"].map(
(type: string) => {
return (
<span key={type} className="flex items-center space-x-1">
<span
className={`inline-block w-3 h-3 rounded ${ProfileUtil.getFrameTypeColor(type)}`}
/>
<span>{type}</span>
</span>
);
},
)}
<span className="font-medium">Code Type:</span>
{[
{ key: "kernel", label: "System / Kernel" },
{ key: "native", label: "Native Code" },
{ key: "jvm", label: "Java / JVM" },
{ key: "cpython", label: "Python" },
{ key: "go", label: "Go" },
{ key: "v8js", label: "JavaScript" },
{ key: "unknown", label: "Other" },
].map((item: { key: string; label: string }) => {
return (
<span key={item.key} className="flex items-center space-x-1">
<span
className={`inline-block w-3 h-3 rounded ${ProfileUtil.getFrameTypeColor(item.key)}`}
/>
<span>{item.label}</span>
</span>
);
})}
</div>
<div
@@ -359,8 +365,8 @@ const ProfileFlamegraph: FunctionComponent<ProfileFlamegraphProps> = (
{tooltip.fileName && (
<div className="text-gray-300">{tooltip.fileName}</div>
)}
<div className="mt-1">Self: {tooltip.selfValue.toLocaleString()}</div>
<div>Total: {tooltip.totalValue.toLocaleString()}</div>
<div className="mt-1">Own Time: {tooltip.selfValue.toLocaleString()}</div>
<div>Total Time: {tooltip.totalValue.toLocaleString()}</div>
</div>
)}
</div>

View File

@@ -204,7 +204,7 @@ const ProfileFunctionList: FunctionComponent<ProfileFunctionListProps> = (
if (samples.length === 0) {
return (
<div className="p-8 text-center text-gray-500">
No profile samples found for this profile.
No performance data found for this profile.
</div>
);
}
@@ -228,7 +228,7 @@ const ProfileFunctionList: FunctionComponent<ProfileFunctionListProps> = (
handleSort("fileName");
}}
>
File{getSortIndicator("fileName")}
Source File{getSortIndicator("fileName")}
</th>
<th
className="px-4 py-3 text-right cursor-pointer hover:bg-gray-100 select-none"
@@ -236,7 +236,7 @@ const ProfileFunctionList: FunctionComponent<ProfileFunctionListProps> = (
handleSort("selfValue");
}}
>
Self Value{getSortIndicator("selfValue")}
Own Time{getSortIndicator("selfValue")}
</th>
<th
className="px-4 py-3 text-right cursor-pointer hover:bg-gray-100 select-none"
@@ -244,7 +244,7 @@ const ProfileFunctionList: FunctionComponent<ProfileFunctionListProps> = (
handleSort("totalValue");
}}
>
Total Value{getSortIndicator("totalValue")}
Total Time{getSortIndicator("totalValue")}
</th>
<th
className="px-4 py-3 text-right cursor-pointer hover:bg-gray-100 select-none"
@@ -252,7 +252,7 @@ const ProfileFunctionList: FunctionComponent<ProfileFunctionListProps> = (
handleSort("sampleCount");
}}
>
Samples{getSortIndicator("sampleCount")}
Occurrences{getSortIndicator("sampleCount")}
</th>
</tr>
</thead>

View File

@@ -28,6 +28,7 @@ import ListResult from "Common/Types/BaseDatabase/ListResult";
import Service from "Common/Models/DatabaseModels/Service";
import { LIMIT_PER_PROJECT } from "Common/Types/Database/LimitMax";
import ServiceElement from "../Service/ServiceElement";
import ProfileUtil from "../../Utils/ProfileUtil";
export interface ComponentProps {
modelId?: ObjectID | undefined;
@@ -196,23 +197,25 @@ const ProfileTable: FunctionComponent<ComponentProps> = (
isDeleteable={false}
isEditable={false}
isCreateable={false}
singularName="Profile"
pluralName="Profiles"
name="Profiles"
singularName="Performance Profile"
pluralName="Performance Profiles"
name="Performance Profiles"
isViewable={true}
cardProps={
props.isMinimalTable
? undefined
: {
title: "Profiles",
title: "Performance Profiles",
description:
"Continuous profiling data from your services. Profiles help you understand CPU, memory, and allocation hotspots in your applications.",
"See where your application spends the most time and memory. Use profiles to find slow functions and optimize performance.",
}
}
query={query}
showViewIdButton={true}
noItemsMessage={
props.noItemsMessage ? props.noItemsMessage : "No profiles found."
props.noItemsMessage
? props.noItemsMessage
: "No performance profiles found. Once your services start sending profiling data, they will appear here."
}
showRefreshButton={true}
sortBy="startTime"
@@ -245,7 +248,7 @@ const ProfileTable: FunctionComponent<ComponentProps> = (
profileType: true,
},
type: FieldType.Text,
title: "Profile Type",
title: "Type",
},
{
field: {
@@ -259,7 +262,7 @@ const ProfileTable: FunctionComponent<ComponentProps> = (
startTime: true,
},
type: FieldType.DateTime,
title: "Start Time",
title: "Captured At",
},
{
field: {
@@ -273,20 +276,6 @@ const ProfileTable: FunctionComponent<ComponentProps> = (
]}
onAdvancedFiltersToggle={handleAdvancedFiltersToggle}
columns={[
{
field: {
profileId: true,
},
title: "Profile ID",
type: FieldType.Text,
},
{
field: {
profileType: true,
},
title: "Profile Type",
type: FieldType.Text,
},
{
field: {
serviceId: true,
@@ -312,18 +301,40 @@ const ProfileTable: FunctionComponent<ComponentProps> = (
);
},
},
{
field: {
profileType: true,
},
title: "Type",
type: FieldType.Element,
getElement: (profile: Profile): ReactElement => {
const profileType: string = profile.profileType || "unknown";
const displayName: string =
ProfileUtil.getProfileTypeDisplayName(profileType);
const badgeColor: string =
ProfileUtil.getProfileTypeBadgeColor(profileType);
return (
<span
className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${badgeColor}`}
>
{displayName}
</span>
);
},
},
{
field: {
sampleCount: true,
},
title: "Samples",
title: "Data Points",
type: FieldType.Number,
},
{
field: {
startTime: true,
},
title: "Start Time",
title: "Captured At",
type: FieldType.DateTime,
},
]}

View File

@@ -168,7 +168,7 @@ const ProfileTimeline: FunctionComponent<ProfileTimelineProps> = (
<div className="w-full">
<div className="flex items-center justify-between mb-2">
<span className="text-xs font-medium text-gray-600">
Profile Density ({profiles.length} profiles)
Activity ({profiles.length} profiles captured)
</span>
<span className="text-xs text-gray-400">
{OneUptimeDate.getDateAsLocalFormattedString(props.startTime, true)} {" "}

View File

@@ -12,12 +12,12 @@ interface ProfileTypeOption {
const profileTypeOptions: Array<ProfileTypeOption> = [
{ label: "All Types", value: undefined },
{ label: "CPU", value: "cpu" },
{ label: "Wall", value: "wall" },
{ label: "Alloc Objects", value: "alloc_objects" },
{ label: "Alloc Space", value: "alloc_space" },
{ label: "Goroutine", value: "goroutine" },
{ label: "Contention", value: "contention" },
{ label: "CPU Usage", value: "cpu" },
{ label: "Wall Clock Time", value: "wall" },
{ label: "Memory Allocations (Count)", value: "alloc_objects" },
{ label: "Memory Allocations (Size)", value: "alloc_space" },
{ label: "Goroutines", value: "goroutine" },
{ label: "Lock Contention", value: "contention" },
];
const ProfileTypeSelector: FunctionComponent<ProfileTypeSelectorProps> = (
@@ -25,7 +25,7 @@ const ProfileTypeSelector: FunctionComponent<ProfileTypeSelectorProps> = (
): ReactElement => {
return (
<div className="flex items-center space-x-2">
<label className="text-sm font-medium text-gray-700">Profile Type:</label>
<label className="text-sm font-medium text-gray-700">Show:</label>
<select
className="px-3 py-1.5 text-sm border border-gray-300 rounded bg-white text-gray-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
value={props.selectedProfileType || ""}

View File

@@ -14,7 +14,7 @@ const ProfilesLayout: FunctionComponent<
return (
<Page
title="Profiles"
title="Performance Profiles"
breadcrumbLinks={getProfilesBreadcrumbs(path)}
sideMenu={<SideMenu />}
>

View File

@@ -10,7 +10,7 @@ import React, { FunctionComponent, ReactElement } from "react";
const DashboardSideMenu: FunctionComponent = (): ReactElement => {
const sections: SideMenuSectionProps[] = [
{
title: "Profiles",
title: "Performance",
items: [
{
link: {
@@ -24,11 +24,11 @@ const DashboardSideMenu: FunctionComponent = (): ReactElement => {
],
},
{
title: "Documentation",
title: "Help",
items: [
{
link: {
title: "Documentation",
title: "Setup Guide",
to: RouteUtil.populateRouteParams(
RouteMap[PageMap.PROFILES_DOCUMENTATION] as Route,
),

View File

@@ -23,7 +23,7 @@ const ProfileViewPage: FunctionComponent<
const tabs: Array<Tab> = [
{
name: "Flamegraph",
name: "Performance Map",
children: (
<ProfileFlamegraph
profileId={profileId}
@@ -32,7 +32,7 @@ const ProfileViewPage: FunctionComponent<
),
},
{
name: "Function List",
name: "Hotspots",
children: (
<ProfileFunctionList
profileId={profileId}
@@ -41,12 +41,13 @@ const ProfileViewPage: FunctionComponent<
),
},
{
name: "Diff",
name: "Compare",
children: (
<div>
<p className="text-sm text-gray-500 mb-4">
Compare profile data between two time ranges. Baseline is the
earlier period, comparison is the more recent period.
Compare performance between two time periods to see what got faster
or slower. The baseline is the earlier period, and the comparison is
the more recent period.
</p>
<DiffFlamegraph
baselineStartTime={twoHoursAgo}

View File

@@ -12,7 +12,7 @@ const ProfilesViewLayout: FunctionComponent<
const path: string = Navigation.getRoutePath(RouteUtil.getRoutes());
return (
<Page
title="Profile Explorer"
title="Profile Details"
breadcrumbLinks={getProfilesBreadcrumbs(path)}
>
<Outlet />

View File

@@ -136,7 +136,7 @@ const DashboardSideMenu: FunctionComponent<ComponentProps> = (
<SideMenuItem
link={{
title: "Profiles",
title: "Performance Profiles",
to: RouteUtil.populateRouteParams(
RouteMap[PageMap.SERVICE_VIEW_PROFILES] as Route,
{ modelId: props.modelId },

View File

@@ -5,16 +5,19 @@ import Link from "Common/Types/Link";
export function getProfilesBreadcrumbs(path: string): Array<Link> | undefined {
const breadcrumpLinksMap: Dictionary<Link[]> = {
...BuildBreadcrumbLinksByTitles(PageMap.PROFILES, ["Project", "Profiles"]),
...BuildBreadcrumbLinksByTitles(PageMap.PROFILES, [
"Project",
"Performance Profiles",
]),
...BuildBreadcrumbLinksByTitles(PageMap.PROFILE_VIEW, [
"Project",
"Profiles",
"Profile Explorer",
"Performance Profiles",
"Profile Details",
]),
...BuildBreadcrumbLinksByTitles(PageMap.PROFILES_DOCUMENTATION, [
"Project",
"Profiles",
"Documentation",
"Performance Profiles",
"Setup Guide",
]),
};
return breadcrumpLinksMap[path];

View File

@@ -5,6 +5,66 @@ export interface ParsedStackFrame {
}
export default class ProfileUtil {
public static getProfileTypeDisplayName(profileType: string): string {
const type: string = profileType.toLowerCase().trim();
switch (type) {
case "cpu":
return "CPU Usage";
case "wall":
return "Wall Clock Time";
case "inuse_objects":
return "Memory Objects in Use";
case "inuse_space":
return "Memory Space in Use";
case "alloc_objects":
return "Memory Allocations (Count)";
case "alloc_space":
return "Memory Allocations (Size)";
case "goroutine":
return "Goroutines";
case "contention":
return "Lock Contention";
case "samples":
return "CPU Samples";
case "mutex":
return "Mutex Contention";
case "block":
return "Blocking Operations";
case "heap":
return "Heap Memory";
default:
return profileType;
}
}
public static getProfileTypeBadgeColor(profileType: string): string {
const type: string = profileType.toLowerCase().trim();
switch (type) {
case "cpu":
case "samples":
return "bg-orange-100 text-orange-800";
case "wall":
return "bg-purple-100 text-purple-800";
case "inuse_objects":
case "inuse_space":
case "alloc_objects":
case "alloc_space":
case "heap":
return "bg-blue-100 text-blue-800";
case "goroutine":
return "bg-green-100 text-green-800";
case "contention":
case "mutex":
case "block":
return "bg-red-100 text-red-800";
default:
return "bg-gray-100 text-gray-800";
}
}
public static getFrameTypeColor(frameType: string): string {
const type: string = frameType.toLowerCase();