mirror of
https://github.com/OneUptime/oneuptime.git
synced 2026-04-06 00:32:12 +02:00
refactor: update terminology and improve messaging for performance profiling components
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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)}%)
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
]}
|
||||
|
||||
@@ -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)} —{" "}
|
||||
|
||||
@@ -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 || ""}
|
||||
|
||||
@@ -14,7 +14,7 @@ const ProfilesLayout: FunctionComponent<
|
||||
|
||||
return (
|
||||
<Page
|
||||
title="Profiles"
|
||||
title="Performance Profiles"
|
||||
breadcrumbLinks={getProfilesBreadcrumbs(path)}
|
||||
sideMenu={<SideMenu />}
|
||||
>
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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 />
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user