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


Reply via email to