This is an automated email from the ASF dual-hosted git repository.
liuhan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/skywalking-cli.git
The following commit(s) were added to refs/heads/master by this push:
new bce7faa Support Async Profiler Feature (#203)
bce7faa is described below
commit bce7faaabbd57ed1f40156af8a8eb6c3eccea4ae
Author: zhengziyi0117 <[email protected]>
AuthorDate: Mon Oct 28 13:15:32 2024 +0800
Support Async Profiler Feature (#203)
---
.github/workflows/CI.yaml | 2 +-
CHANGES.md | 2 +
.../profiling/asyncprofiler/CreateTask.graphql | 24 ++++++
.../profiling/asyncprofiler/GetAnalysis.graphql | 31 +++++++
.../profiling/asyncprofiler/GetTaskList.graphql | 31 +++++++
.../asyncprofiler/GetTaskProgress.graphql | 30 +++++++
dist/LICENSE | 2 +-
go.mod | 2 +-
go.sum | 4 +-
internal/commands/interceptor/instance.go | 99 +++++++++++++++++++---
.../asyncprofiler.go} | 26 +++---
.../commands/profiling/asyncprofiler/create.go | 95 +++++++++++++++++++++
.../commands/profiling/asyncprofiler/getAnalyze.go | 85 +++++++++++++++++++
.../profiling/asyncprofiler/getTaskList.go | 90 ++++++++++++++++++++
.../profiling/asyncprofiler/getTaskProgress.go | 55 ++++++++++++
internal/commands/profiling/profiling.go | 2 +
internal/flags/instance.go | 15 ++++
.../model/asyncprofiler/asyncProfilerEventType.go | 63 ++++++++++++++
.../asyncprofiler/jfrEventType.go} | 41 +++++----
pkg/graphql/profiling/asyncprofiler.go | 71 ++++++++++++++++
test/cases/basic/expected/dependency-endpoint.yml | 1 +
test/cases/basic/expected/dependency-instance.yml | 1 +
test/cases/basic/expected/dependency-service.yml | 1 +
test/cases/basic/expected/trace-users-detail.yml | 1 +
test/cases/basic/expected/traces-list.yml | 1 +
25 files changed, 727 insertions(+), 48 deletions(-)
diff --git a/.github/workflows/CI.yaml b/.github/workflows/CI.yaml
index 423387a..1b24526 100644
--- a/.github/workflows/CI.yaml
+++ b/.github/workflows/CI.yaml
@@ -90,7 +90,7 @@ jobs:
go-version: 1.18
- name: golangci-lint
- uses: golangci/golangci-lint-action@v3
+ uses: golangci/golangci-lint-action@v6
with:
version: v1.50.0
args: --timeout 5m
diff --git a/CHANGES.md b/CHANGES.md
index a7323dc..410cea7 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -7,6 +7,8 @@ Release Notes.
### Features
+* add the sub-command `profiling async` for async-profiler query API by
@zhengziyi0117 in https://github.com/apache/skywalking-cli/pull/203
+
### Bug Fixes
0.14.0
diff --git a/assets/graphqls/profiling/asyncprofiler/CreateTask.graphql
b/assets/graphqls/profiling/asyncprofiler/CreateTask.graphql
new file mode 100644
index 0000000..2e53fbd
--- /dev/null
+++ b/assets/graphqls/profiling/asyncprofiler/CreateTask.graphql
@@ -0,0 +1,24 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+mutation ($condition: AsyncProfilerTaskCreationRequest!) {
+ result: createAsyncProfilerTask(asyncProfilerTaskCreationRequest:
$condition) {
+ errorReason
+ code
+ id
+ }
+}
\ No newline at end of file
diff --git a/assets/graphqls/profiling/asyncprofiler/GetAnalysis.graphql
b/assets/graphqls/profiling/asyncprofiler/GetAnalysis.graphql
new file mode 100644
index 0000000..a8cd925
--- /dev/null
+++ b/assets/graphqls/profiling/asyncprofiler/GetAnalysis.graphql
@@ -0,0 +1,31 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+query ($condition: AsyncProfilerAnalyzationRequest!) {
+ result: queryAsyncProfilerAnalyze(request: $condition) {
+ tree {
+ type
+ elements {
+ id
+ parentId
+ codeSignature
+ total
+ self
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/assets/graphqls/profiling/asyncprofiler/GetTaskList.graphql
b/assets/graphqls/profiling/asyncprofiler/GetTaskList.graphql
new file mode 100644
index 0000000..f62ca30
--- /dev/null
+++ b/assets/graphqls/profiling/asyncprofiler/GetTaskList.graphql
@@ -0,0 +1,31 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+query ($condition: AsyncProfilerTaskListRequest!) {
+ result: queryAsyncProfilerTaskList(request: $condition) {
+ errorReason
+ tasks {
+ serviceId
+ serviceInstanceIds
+ createTime
+ events
+ duration
+ execArgs
+ id
+ }
+ }
+}
diff --git a/assets/graphqls/profiling/asyncprofiler/GetTaskProgress.graphql
b/assets/graphqls/profiling/asyncprofiler/GetTaskProgress.graphql
new file mode 100644
index 0000000..a81c4c0
--- /dev/null
+++ b/assets/graphqls/profiling/asyncprofiler/GetTaskProgress.graphql
@@ -0,0 +1,30 @@
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you under
+# the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+query ($taskId: String!){
+ result: queryAsyncProfilerTaskProgress(taskId: $taskId) {
+ errorInstanceIds
+ successInstanceIds
+ logs {
+ id
+ instanceId
+ instanceName
+ operationType
+ operationTime
+ }
+ }
+}
\ No newline at end of file
diff --git a/dist/LICENSE b/dist/LICENSE
index b53f5be..4bb722f 100644
--- a/dist/LICENSE
+++ b/dist/LICENSE
@@ -213,7 +213,7 @@ The text of each license is also included at
licenses/license-[project].txt.
k8s.io/utils v0.0.0-20210802155522-efc7438f0176 Apache-2.0
sigs.k8s.io/controller-runtime v0.10.0 Apache-2.0
sigs.k8s.io/structured-merge-diff/v4 v4.1.2 Apache-2.0
- skywalking.apache.org/repo/goapi v0.0.0-20240227092755-edee3273b361
Apache-2.0
+ skywalking.apache.org/repo/goapi v0.0.0-20241023080050-2514649a8007
Apache-2.0
========================================================================
BSD-2-Clause licenses
diff --git a/go.mod b/go.mod
index c030ac6..4a771a3 100644
--- a/go.mod
+++ b/go.mod
@@ -18,7 +18,7 @@ require (
gopkg.in/yaml.v2 v2.4.0
k8s.io/apimachinery v0.22.1
sigs.k8s.io/controller-runtime v0.10.0
- skywalking.apache.org/repo/goapi v0.0.0-20240227092755-edee3273b361
+ skywalking.apache.org/repo/goapi v0.0.0-20241023080050-2514649a8007
)
require (
diff --git a/go.sum b/go.sum
index c815258..14f447f 100644
--- a/go.sum
+++ b/go.sum
@@ -879,5 +879,5 @@ sigs.k8s.io/structured-merge-diff/v4 v4.1.2
h1:Hr/htKFmJEbtMgS/UD0N+gtgctAqz81t3
sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod
h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
-skywalking.apache.org/repo/goapi v0.0.0-20240227092755-edee3273b361
h1:FCGGU4Tut3LI/zMRXSgJgUL/kmSQ4b7QktFgRBhqaDs=
-skywalking.apache.org/repo/goapi v0.0.0-20240227092755-edee3273b361/go.mod
h1:+n8BMuS8eRdzdnGh15ElRGBXPi0eYZSs2TKySBDmRTE=
+skywalking.apache.org/repo/goapi v0.0.0-20241023080050-2514649a8007
h1:MQU9yOZxgbs7fU145wd400/E3ES/HWoxTtTCudaCBoA=
+skywalking.apache.org/repo/goapi v0.0.0-20241023080050-2514649a8007/go.mod
h1:+n8BMuS8eRdzdnGh15ElRGBXPi0eYZSs2TKySBDmRTE=
diff --git a/internal/commands/interceptor/instance.go
b/internal/commands/interceptor/instance.go
index 78f5ad1..d381717 100644
--- a/internal/commands/interceptor/instance.go
+++ b/internal/commands/interceptor/instance.go
@@ -30,6 +30,8 @@ const (
instanceNameFlagName = "instance-name"
destInstanceIDFlagName = "dest-instance-id"
destInstanceNameFlagName = "dest-instance-name"
+ InstanceIDListFlagName = "instance-id-list"
+ instanceNameListFlagName = "instance-name-list"
)
// ParseInstance parses the service instance id or service instance name,
@@ -44,6 +46,18 @@ func ParseInstance(required bool) func(*cli.Context) error {
}
}
+// ParseInstanceList parses the service instance id slice or service instance
name slice,
+// and converts the present one to the missing one.
+// See flags.InstanceSliceFlags.
+func ParseInstanceList(required bool) func(*cli.Context) error {
+ return func(ctx *cli.Context) error {
+ if err := ParseService(required)(ctx); err != nil {
+ return err
+ }
+ return parseInstanceList(required, InstanceIDListFlagName,
instanceNameListFlagName, serviceIDFlagName)(ctx)
+ }
+}
+
// ParseInstanceRelation parses the source and destination service instance id
or service instance name,
// and converts the present one to the missing one respectively.
// See flags.InstanceRelationFlags.
@@ -72,26 +86,85 @@ func parseInstance(required bool, idFlagName, nameFlagName,
serviceIDFlagName st
return nil
}
- if id != "" {
- parts := strings.Split(id, "_")
- if len(parts) != 2 {
- return fmt.Errorf("invalid instance id, cannot
be splitted into 2 parts. %v", id)
+ id, name, err := encode(serviceID, nameFlagName, id, name)
+ if err != nil {
+ return err
+ }
+
+ if err := ctx.Set(idFlagName, id); err != nil {
+ return err
+ }
+ return ctx.Set(nameFlagName, name)
+ }
+}
+
+func parseInstanceList(required bool, idListFlagName, nameListFlagName,
serviceIDFlagName string) func(*cli.Context) error {
+ return func(ctx *cli.Context) error {
+ idsArg := ctx.String(idListFlagName)
+ namesArgs := ctx.String(nameListFlagName)
+ serviceID := ctx.String(serviceIDFlagName)
+
+ if idsArg == "" && namesArgs == "" {
+ if required {
+ return fmt.Errorf(`either flags "--%s" or
"--%s" must be given`, idListFlagName, nameListFlagName)
+ }
+ return nil
+ }
+
+ ids := strings.Split(idsArg, ",")
+ names := strings.Split(namesArgs, ",")
+ var sliceSize int
+ if l := len(ids); idsArg != "" && l != 0 {
+ sliceSize = l
+ } else {
+ sliceSize = len(names)
+ }
+ instanceIDSlice := make([]string, sliceSize)
+ instanceNameSlice := make([]string, sliceSize)
+ for i := 0; i < sliceSize; i++ {
+ id := ""
+ name := ""
+ if len(ids) > i {
+ id = ids[i]
+ }
+ if len(names) > i {
+ name = names[i]
}
- s, err := base64.StdEncoding.DecodeString(parts[1])
+
+ id, name, err := encode(serviceID, nameListFlagName,
id, name)
if err != nil {
return err
}
- name = string(s)
- } else if name != "" {
- if serviceID == "" {
- return fmt.Errorf(`"--%s" is specified but its
related service name or id is not given`, nameFlagName)
- }
- id = serviceID + "_" + b64enc(name)
+
+ instanceIDSlice[i] = id
+ instanceNameSlice[i] = name
}
- if err := ctx.Set(idFlagName, id); err != nil {
+ instanceIDSliceString := strings.Join(instanceIDSlice, ",")
+ instanceNameSliceString := strings.Join(instanceNameSlice, ",")
+ if err := ctx.Set(idListFlagName, instanceIDSliceString); err
!= nil {
return err
}
- return ctx.Set(nameFlagName, name)
+ return ctx.Set(nameListFlagName, instanceNameSliceString)
+ }
+}
+
+func encode(serviceID, nameFlagName, id, name string) (encodedID, encodedName
string, err error) {
+ if id != "" {
+ parts := strings.Split(id, "_")
+ if len(parts) != 2 {
+ return "", "", fmt.Errorf("invalid instance id, cannot
be splitted into 2 parts. %v", id)
+ }
+ s, err := base64.StdEncoding.DecodeString(parts[1])
+ if err != nil {
+ return "", "", err
+ }
+ name = string(s)
+ } else if name != "" {
+ if serviceID == "" {
+ return "", "", fmt.Errorf(`"--%s" is specified but its
related service name or id is not given`, nameFlagName)
+ }
+ id = serviceID + "_" + b64enc(name)
}
+ return id, name, nil
}
diff --git a/internal/commands/profiling/profiling.go
b/internal/commands/profiling/asyncprofiler/asyncprofiler.go
similarity index 62%
copy from internal/commands/profiling/profiling.go
copy to internal/commands/profiling/asyncprofiler/asyncprofiler.go
index 6059b76..e68086c 100644
--- a/internal/commands/profiling/profiling.go
+++ b/internal/commands/profiling/asyncprofiler/asyncprofiler.go
@@ -15,24 +15,20 @@
// specific language governing permissions and limitations
// under the License.
-package profiling
+package asyncprofiler
-import (
- "github.com/urfave/cli/v2"
-
-
"github.com/apache/skywalking-cli/internal/commands/profiling/continuous"
- "github.com/apache/skywalking-cli/internal/commands/profiling/ebpf"
- "github.com/apache/skywalking-cli/internal/commands/profiling/trace"
-)
+import "github.com/urfave/cli/v2"
var Command = &cli.Command{
- Name: "profiling",
- Usage: "profiling related sub-command",
- UsageText: `If your application has performance issue, you could try to
profiling.
-Please following sub-command to get more information.`,
+ Name: "async",
+ Usage: "async profiler related sub-command",
+ UsageText: `If your endpoint has performance issue and could not use
tracing to find out what's happening,
+you could try it. You could get more information
+on
https://skywalking.apache.org/docs/main/next/en/concepts-and-designs/profiling.`,
Subcommands: []*cli.Command{
- trace.Command,
- ebpf.Command,
- continuous.Command,
+ createCommand,
+ getTaskListCommand,
+ getTaskProgressCommand,
+ analysisCommand,
},
}
diff --git a/internal/commands/profiling/asyncprofiler/create.go
b/internal/commands/profiling/asyncprofiler/create.go
new file mode 100644
index 0000000..1d21938
--- /dev/null
+++ b/internal/commands/profiling/asyncprofiler/create.go
@@ -0,0 +1,95 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package asyncprofiler
+
+import (
+ "strings"
+
+ "github.com/urfave/cli/v2"
+ "skywalking.apache.org/repo/goapi/query"
+
+ "github.com/apache/skywalking-cli/internal/commands/interceptor"
+ "github.com/apache/skywalking-cli/internal/flags"
+ "github.com/apache/skywalking-cli/internal/model/asyncprofiler"
+ "github.com/apache/skywalking-cli/pkg/display"
+ "github.com/apache/skywalking-cli/pkg/display/displayable"
+ "github.com/apache/skywalking-cli/pkg/graphql/profiling"
+)
+
+var createCommand = &cli.Command{
+ Name: "create",
+ Aliases: []string{"c"},
+ Usage: "Create a new async profiler task",
+ UsageText: `Create a new async profiler task
+
+Examples:
+1. Create async-profiler task
+$ swctl profiling async create --service-name=service-name --duration=60
--events=cpu,alloc \
+ --instance-name-list=instance-name1,instance-name2
--exec-args=interval=50ms`,
+ Flags: flags.Flags(
+ flags.ServiceFlags,
+ flags.InstanceListFlags,
+ []cli.Flag{
+ &cli.IntFlag{
+ Name: "duration",
+ Usage: "task continuous time(second).",
+ Required: true,
+ },
+ &cli.GenericFlag{
+ Name: "events",
+ Usage: "which event types this task needs to
collect.",
+ Required: true,
+ Value:
&asyncprofiler.ProfilerEventTypeEnumValue{
+ Enum: query.AllAsyncProfilerEventType,
+ },
+ },
+ &cli.StringFlag{
+ Name: "exec-args",
+ Usage: "other async-profiler execution options,
e.g. alloc=2k,lock=2s.",
+ },
+ },
+ ),
+ Before: interceptor.BeforeChain(
+ interceptor.ParseInstanceList(true),
+ ),
+ Action: func(ctx *cli.Context) error {
+ serviceID := ctx.String("service-id")
+ instanceIds := strings.Split(ctx.String("instance-id-list"),
",")
+ duration := ctx.Int("duration")
+ eventTypes :=
ctx.Generic("events").(*asyncprofiler.ProfilerEventTypeEnumValue).Selected
+
+ var execArgs *string
+ if args := ctx.String("exec-args"); args != "" {
+ execArgs = &args
+ }
+
+ request := &query.AsyncProfilerTaskCreationRequest{
+ ServiceID: serviceID,
+ ServiceInstanceIds: instanceIds,
+ Duration: duration,
+ Events: eventTypes,
+ ExecArgs: execArgs,
+ }
+ task, err := profiling.CreateAsyncProfilerTask(ctx, request)
+ if err != nil {
+ return err
+ }
+
+ return display.Display(ctx, &displayable.Displayable{Data:
task, Condition: request})
+ },
+}
diff --git a/internal/commands/profiling/asyncprofiler/getAnalyze.go
b/internal/commands/profiling/asyncprofiler/getAnalyze.go
new file mode 100644
index 0000000..c1b0387
--- /dev/null
+++ b/internal/commands/profiling/asyncprofiler/getAnalyze.go
@@ -0,0 +1,85 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package asyncprofiler
+
+import (
+ "strings"
+
+ "github.com/urfave/cli/v2"
+ "skywalking.apache.org/repo/goapi/query"
+
+ "github.com/apache/skywalking-cli/internal/commands/interceptor"
+ "github.com/apache/skywalking-cli/internal/flags"
+ "github.com/apache/skywalking-cli/internal/model/asyncprofiler"
+ "github.com/apache/skywalking-cli/pkg/display"
+ "github.com/apache/skywalking-cli/pkg/display/displayable"
+ "github.com/apache/skywalking-cli/pkg/graphql/profiling"
+)
+
+var analysisCommand = &cli.Command{
+ Name: "analysis",
+ Aliases: []string{"a"},
+ Usage: "Query async-profiler analysis",
+ UsageText: `Query async-profiler analysis
+
+Examples:
+1. Query the flame graph produced by async-profiler
+$ swctl profiling async analysis --service-name=service-name --task-id=task-id
\
+ --instance-name-list=instance-name1,instance-name2
--event=execution_sample`,
+ Flags: flags.Flags(
+ flags.ServiceFlags,
+ flags.InstanceListFlags,
+ []cli.Flag{
+ &cli.StringFlag{
+ Name: "task-id",
+ Usage: "async-profiler task id",
+ Required: true,
+ },
+ &cli.GenericFlag{
+ Name: "event",
+ Usage: "which event types this task needs to
collect.",
+ Required: true,
+ Value: &asyncprofiler.JFREventTypeEnumValue{
+ Enum: query.AllJFREventType,
+ },
+ },
+ },
+ ),
+ Before: interceptor.BeforeChain(
+ interceptor.ParseInstanceList(true),
+ ),
+ Action: func(ctx *cli.Context) error {
+ taskID := ctx.String("task-id")
+ instances := strings.Split(ctx.String("instance-id-list"), ",")
+ eventType :=
ctx.Generic("event").(*asyncprofiler.JFREventTypeEnumValue).Selected
+
+ request := &query.AsyncProfilerAnalyzationRequest{
+ TaskID: taskID,
+ InstanceIds: instances,
+ EventType: eventType,
+ }
+
+ analyze, err := profiling.GetAsyncProfilerAnalyze(ctx, request)
+
+ if err != nil {
+ return err
+ }
+
+ return display.Display(ctx, &displayable.Displayable{Data:
analyze, Condition: request})
+ },
+}
diff --git a/internal/commands/profiling/asyncprofiler/getTaskList.go
b/internal/commands/profiling/asyncprofiler/getTaskList.go
new file mode 100644
index 0000000..067a3e2
--- /dev/null
+++ b/internal/commands/profiling/asyncprofiler/getTaskList.go
@@ -0,0 +1,90 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package asyncprofiler
+
+import (
+ "github.com/urfave/cli/v2"
+ "skywalking.apache.org/repo/goapi/query"
+
+ "github.com/apache/skywalking-cli/internal/commands/interceptor"
+ "github.com/apache/skywalking-cli/internal/flags"
+ "github.com/apache/skywalking-cli/pkg/display"
+ "github.com/apache/skywalking-cli/pkg/display/displayable"
+ "github.com/apache/skywalking-cli/pkg/graphql/profiling"
+)
+
+var getTaskListCommand = &cli.Command{
+ Name: "list",
+ Aliases: []string{"l"},
+ Usage: "Query async-profiler task list",
+ UsageText: `Query async-profiler task list
+
+Examples:
+1. Query all async-profiler tasks
+$ swctl profiling async list --service-name=service-name`,
+ Flags: flags.Flags(
+ flags.ServiceFlags,
+ []cli.Flag{
+ &cli.Int64Flag{
+ Name: "start-time",
+ Usage: "The start time (in milliseconds) of the
event, measured between the current time and midnight, January 1, 1970 UTC.",
+ },
+ &cli.Int64Flag{
+ Name: "end-time",
+ Usage: "The end time (in milliseconds) of the
event, measured between the current time and midnight, January 1, 1970 UTC.",
+ },
+ &cli.IntFlag{
+ Name: "limit",
+ Usage: "Limit defines the number of the tasks
to be returned.",
+ },
+ },
+ ),
+ Before: interceptor.BeforeChain(
+ interceptor.ParseService(true),
+ ),
+ Action: func(ctx *cli.Context) error {
+ serviceID := ctx.String("service-id")
+ var startTime *int64
+ if startTimeArg := ctx.Int64("start-time"); startTimeArg != 0 {
+ startTime = &startTimeArg
+ }
+ var endTime *int64
+ if endTimeArg := ctx.Int64("end-time"); endTimeArg != 0 {
+ endTime = &endTimeArg
+ }
+ var limit *int
+ if limitArg := ctx.Int("limit"); limitArg != 0 {
+ limit = &limitArg
+ }
+
+ request := &query.AsyncProfilerTaskListRequest{
+ ServiceID: serviceID,
+ StartTime: startTime,
+ EndTime: endTime,
+ Limit: limit,
+ }
+
+ tasks, err := profiling.GetAsyncProfilerTaskList(ctx, request)
+
+ if err != nil {
+ return err
+ }
+
+ return display.Display(ctx, &displayable.Displayable{Data:
tasks, Condition: request})
+ },
+}
diff --git a/internal/commands/profiling/asyncprofiler/getTaskProgress.go
b/internal/commands/profiling/asyncprofiler/getTaskProgress.go
new file mode 100644
index 0000000..3943e84
--- /dev/null
+++ b/internal/commands/profiling/asyncprofiler/getTaskProgress.go
@@ -0,0 +1,55 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package asyncprofiler
+
+import (
+ "github.com/urfave/cli/v2"
+
+ "github.com/apache/skywalking-cli/pkg/display"
+ "github.com/apache/skywalking-cli/pkg/display/displayable"
+ "github.com/apache/skywalking-cli/pkg/graphql/profiling"
+)
+
+var getTaskProgressCommand = &cli.Command{
+ Name: "progress",
+ Aliases: []string{"p"},
+ Flags: []cli.Flag{
+ &cli.StringFlag{
+ Name: "task-id",
+ Usage: "async profiler task id.",
+ Required: true,
+ },
+ },
+ Usage: "Query async-profiler task progress",
+ UsageText: `Query async-profiler task progress
+
+Examples:
+1. Query task progress, including task logs and successInstances and
errorInstances
+$ swctl profiling async progress --task-id=task-id`,
+ Action: func(ctx *cli.Context) error {
+ taskID := ctx.String("task-id")
+
+ data, err := profiling.GetAsyncProfilerTaskProgress(ctx, taskID)
+
+ if err != nil {
+ return err
+ }
+
+ return display.Display(ctx, &displayable.Displayable{Data:
data, Condition: taskID})
+ },
+}
diff --git a/internal/commands/profiling/profiling.go
b/internal/commands/profiling/profiling.go
index 6059b76..54144b6 100644
--- a/internal/commands/profiling/profiling.go
+++ b/internal/commands/profiling/profiling.go
@@ -20,6 +20,7 @@ package profiling
import (
"github.com/urfave/cli/v2"
+
"github.com/apache/skywalking-cli/internal/commands/profiling/asyncprofiler"
"github.com/apache/skywalking-cli/internal/commands/profiling/continuous"
"github.com/apache/skywalking-cli/internal/commands/profiling/ebpf"
"github.com/apache/skywalking-cli/internal/commands/profiling/trace"
@@ -34,5 +35,6 @@ Please following sub-command to get more information.`,
trace.Command,
ebpf.Command,
continuous.Command,
+ asyncprofiler.Command,
},
}
diff --git a/internal/flags/instance.go b/internal/flags/instance.go
index 0a5aa44..af3b254 100644
--- a/internal/flags/instance.go
+++ b/internal/flags/instance.go
@@ -52,3 +52,18 @@ var InstanceRelationFlags = append(
Required: false,
},
)
+
+// InstanceListFlags take either service instance id list or service instance
name list as input,
+// and transform to the other one.
+var InstanceListFlags = []cli.Flag{
+ &cli.StringFlag{
+ Name: "instance-id-list",
+ Usage: "`instance id list`, if you don't have instance id
list, use `--instances-name` instead",
+ Required: false,
+ },
+ &cli.StringFlag{
+ Name: "instance-name-list",
+ Usage: "`instance name list`, if you already have instance
id list, prefer to use `--instances-id`",
+ Required: false,
+ },
+}
diff --git a/internal/model/asyncprofiler/asyncProfilerEventType.go
b/internal/model/asyncprofiler/asyncProfilerEventType.go
new file mode 100644
index 0000000..da3db0c
--- /dev/null
+++ b/internal/model/asyncprofiler/asyncProfilerEventType.go
@@ -0,0 +1,63 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package asyncprofiler
+
+import (
+ "fmt"
+ "strings"
+
+ api "skywalking.apache.org/repo/goapi/query"
+)
+
+type ProfilerEventTypeEnumValue struct {
+ Enum []api.AsyncProfilerEventType
+ Default []api.AsyncProfilerEventType
+ Selected []api.AsyncProfilerEventType
+}
+
+func (e *ProfilerEventTypeEnumValue) Set(value string) error {
+ values := strings.Split(value, ",")
+ types := make([]api.AsyncProfilerEventType, 0)
+ for _, v := range values {
+ for _, enum := range e.Enum {
+ if strings.EqualFold(enum.String(), v) {
+ types = append(types, enum)
+ break
+ }
+ }
+ }
+
+ if len(types) != 0 {
+ e.Selected = types
+ return nil
+ }
+
+ orders := make([]string, len(api.AllAsyncProfilerEventType))
+ for i, order := range api.AllAsyncProfilerEventType {
+ orders[i] = order.String()
+ }
+ return fmt.Errorf("allowed analysis aggregate type are %s",
strings.Join(orders, ", "))
+}
+
+func (e *ProfilerEventTypeEnumValue) String() string {
+ selected := make([]string, len(e.Selected))
+ for i, item := range e.Selected {
+ selected[i] = item.String()
+ }
+ return strings.Join(selected, ",")
+}
diff --git a/internal/commands/profiling/profiling.go
b/internal/model/asyncprofiler/jfrEventType.go
similarity index 55%
copy from internal/commands/profiling/profiling.go
copy to internal/model/asyncprofiler/jfrEventType.go
index 6059b76..a216872 100644
--- a/internal/commands/profiling/profiling.go
+++ b/internal/model/asyncprofiler/jfrEventType.go
@@ -15,24 +15,35 @@
// specific language governing permissions and limitations
// under the License.
-package profiling
+package asyncprofiler
import (
- "github.com/urfave/cli/v2"
+ "fmt"
+ "strings"
-
"github.com/apache/skywalking-cli/internal/commands/profiling/continuous"
- "github.com/apache/skywalking-cli/internal/commands/profiling/ebpf"
- "github.com/apache/skywalking-cli/internal/commands/profiling/trace"
+ api "skywalking.apache.org/repo/goapi/query"
)
-var Command = &cli.Command{
- Name: "profiling",
- Usage: "profiling related sub-command",
- UsageText: `If your application has performance issue, you could try to
profiling.
-Please following sub-command to get more information.`,
- Subcommands: []*cli.Command{
- trace.Command,
- ebpf.Command,
- continuous.Command,
- },
+type JFREventTypeEnumValue struct {
+ Enum []api.JFREventType
+ Default api.JFREventType
+ Selected api.JFREventType
+}
+
+func (e *JFREventTypeEnumValue) Set(value string) error {
+ for _, enum := range e.Enum {
+ if strings.EqualFold(enum.String(), value) {
+ e.Selected = enum
+ return nil
+ }
+ }
+ orders := make([]string, len(api.AllJFREventType))
+ for i, order := range api.AllJFREventType {
+ orders[i] = order.String()
+ }
+ return fmt.Errorf("allowed analysis aggregate type are %s",
strings.Join(orders, ", "))
+}
+
+func (e *JFREventTypeEnumValue) String() string {
+ return e.Selected.String()
}
diff --git a/pkg/graphql/profiling/asyncprofiler.go
b/pkg/graphql/profiling/asyncprofiler.go
new file mode 100644
index 0000000..5db697f
--- /dev/null
+++ b/pkg/graphql/profiling/asyncprofiler.go
@@ -0,0 +1,71 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package profiling
+
+import (
+ "github.com/machinebox/graphql"
+ "github.com/urfave/cli/v2"
+ api "skywalking.apache.org/repo/goapi/query"
+
+ "github.com/apache/skywalking-cli/assets"
+ "github.com/apache/skywalking-cli/pkg/graphql/client"
+)
+
+func CreateAsyncProfilerTask(ctx *cli.Context, condition
*api.AsyncProfilerTaskCreationRequest) (api.AsyncProfilerTaskCreationResult,
error) {
+ var response map[string]api.AsyncProfilerTaskCreationResult
+
+ request :=
graphql.NewRequest(assets.Read("graphqls/profiling/asyncprofiler/CreateTask.graphql"))
+ request.Var("condition", condition)
+
+ err := client.ExecuteQuery(ctx, request, &response)
+
+ return response["result"], err
+}
+
+func GetAsyncProfilerTaskList(ctx *cli.Context, condition
*api.AsyncProfilerTaskListRequest) (api.AsyncProfilerTaskListResult, error) {
+ var response map[string]api.AsyncProfilerTaskListResult
+
+ request :=
graphql.NewRequest(assets.Read("graphqls/profiling/asyncprofiler/GetTaskList.graphql"))
+ request.Var("condition", condition)
+
+ err := client.ExecuteQuery(ctx, request, &response)
+
+ return response["result"], err
+}
+
+func GetAsyncProfilerTaskProgress(ctx *cli.Context, taskID string)
(api.AsyncProfilerTaskProgress, error) {
+ var response map[string]api.AsyncProfilerTaskProgress
+
+ request :=
graphql.NewRequest(assets.Read("graphqls/profiling/asyncprofiler/GetTaskProgress.graphql"))
+ request.Var("taskId", taskID)
+
+ err := client.ExecuteQuery(ctx, request, &response)
+
+ return response["result"], err
+}
+
+func GetAsyncProfilerAnalyze(ctx *cli.Context, condition
*api.AsyncProfilerAnalyzationRequest) (api.AsyncProfilerAnalyzation, error) {
+ var response map[string]api.AsyncProfilerAnalyzation
+
+ request :=
graphql.NewRequest(assets.Read("graphqls/profiling/asyncprofiler/GetAnalysis.graphql"))
+ request.Var("condition", condition)
+
+ err := client.ExecuteQuery(ctx, request, &response)
+
+ return response["result"], err
+}
diff --git a/test/cases/basic/expected/dependency-endpoint.yml
b/test/cases/basic/expected/dependency-endpoint.yml
index b8d11d0..e2d8453 100644
--- a/test/cases/basic/expected/dependency-endpoint.yml
+++ b/test/cases/basic/expected/dependency-endpoint.yml
@@ -28,6 +28,7 @@ nodes:
serviceid: {{ b64enc "consumer" }}.1
servicename: consumer
{{- end }}
+debuggingtrace: null
calls:
{{- contains .calls }}
- source: {{ b64enc "consumer" }}.1_{{ b64enc "/users" }}
diff --git a/test/cases/basic/expected/dependency-instance.yml
b/test/cases/basic/expected/dependency-instance.yml
index bfa7f05..33dd279 100644
--- a/test/cases/basic/expected/dependency-instance.yml
+++ b/test/cases/basic/expected/dependency-instance.yml
@@ -41,3 +41,4 @@ calls:
- CLIENT
{{- end }}
{{- end }}
+debuggingtrace: null
diff --git a/test/cases/basic/expected/dependency-service.yml
b/test/cases/basic/expected/dependency-service.yml
index 6bf530f..41c789e 100644
--- a/test/cases/basic/expected/dependency-service.yml
+++ b/test/cases/basic/expected/dependency-service.yml
@@ -41,3 +41,4 @@ calls:
- CLIENT
- SERVER
{{- end }}
+debuggingtrace: null
\ No newline at end of file
diff --git a/test/cases/basic/expected/trace-users-detail.yml
b/test/cases/basic/expected/trace-users-detail.yml
index ab8b888..a286162 100644
--- a/test/cases/basic/expected/trace-users-detail.yml
+++ b/test/cases/basic/expected/trace-users-detail.yml
@@ -100,3 +100,4 @@ spans:
logs: [ ]
attachedevents: []
{{- end }}
+debuggingtrace: null
\ No newline at end of file
diff --git a/test/cases/basic/expected/traces-list.yml
b/test/cases/basic/expected/traces-list.yml
index 3150040..a51cb7f 100644
--- a/test/cases/basic/expected/traces-list.yml
+++ b/test/cases/basic/expected/traces-list.yml
@@ -24,3 +24,4 @@ traces:
traceids:
- {{ index .traceids 0 }}
{{- end }}
+debuggingtrace: null
\ No newline at end of file