From dc6fe74a84fc357934721e58b5338acde47fd0f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Narajowski?= Date: Mon, 25 May 2020 14:13:14 +0200 Subject: [PATCH] Add support for Memfault commands https://memfault.com/ Adds support for pulling coredump data, events and traces from the device. --- newtmgr/cli/commands.go | 1 + newtmgr/cli/memfault.go | 102 ++++++++++++++++++++++++++++++++++++++++ nmxact/nmp/decode.go | 3 ++ nmxact/nmp/defs.go | 28 ++++++----- nmxact/nmp/memfault.go | 49 +++++++++++++++++++ nmxact/xact/memfault.go | 88 ++++++++++++++++++++++++++++++++++ 6 files changed, 260 insertions(+), 11 deletions(-) create mode 100644 newtmgr/cli/memfault.go create mode 100644 nmxact/nmp/memfault.go create mode 100644 nmxact/xact/memfault.go diff --git a/newtmgr/cli/commands.go b/newtmgr/cli/commands.go index 1f37c0d3..6fdb8071 100644 --- a/newtmgr/cli/commands.go +++ b/newtmgr/cli/commands.go @@ -121,6 +121,7 @@ func Commands() *cobra.Command { nmCmd.AddCommand(resCmd()) nmCmd.AddCommand(interactiveCmd()) nmCmd.AddCommand(shellCmd()) + nmCmd.AddCommand(memfaultCmd()) return nmCmd } diff --git a/newtmgr/cli/memfault.go b/newtmgr/cli/memfault.go new file mode 100644 index 00000000..b560882a --- /dev/null +++ b/newtmgr/cli/memfault.go @@ -0,0 +1,102 @@ +/** + * Licensed to the 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. The 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 cli + +import ( + "fmt" + "io/ioutil" + + "github.com/spf13/cobra" + + "mynewt.apache.org/newt/util" + "mynewt.apache.org/newtmgr/newtmgr/nmutil" + "mynewt.apache.org/newtmgr/nmxact/nmp" + "mynewt.apache.org/newtmgr/nmxact/xact" +) + +func memfaultDownloadCmd(cmd *cobra.Command, args []string) { + progressBytes := 0 + file, err := ioutil.TempFile("", "memfault") + if err != nil { + nmUsage(cmd, util.NewNewtError(fmt.Sprintf( + "Cannot open file %s - %s", file.Name(), err.Error()))) + } + + s, err := GetSesn() + if err != nil { + nmUsage(nil, err) + } + + c := xact.NewMemfaultPullCmd() + c.SetTxOptions(nmutil.TxOptions()) + c.ProgressCb = func(c *xact.MemfaultPullCmd, rsp *nmp.MemfaultPullRsp) { + progressBytes += len(rsp.Data) + fmt.Printf("Pulled %d bytes\n", progressBytes) + if _, err := file.Write(rsp.Data); err != nil { + nmUsage(nil, util.ChildNewtError(err)) + } + } + + res, err := c.Run(s) + if err != nil { + nmUsage(nil, util.ChildNewtError(err)) + } + + sres := res.(*xact.MemfaultPullResult) + if sres.Status() != 0 { + fmt.Printf("Error: %d\n", sres.Status()) + return + } + + err = file.Sync() + if err != nil { + nmUsage(nil, util.ChildNewtError(err)) + } + + err = file.Close() + if err != nil { + nmUsage(nil, util.ChildNewtError(err)) + } + + fmt.Printf("Done writing memfault file to %s\n", file.Name()) +} + +func memfaultCmd() *cobra.Command { + memfaultCmd := &cobra.Command{ + Use: "memfault", + Short: "Manage Memfault data on a device", + Run: func(cmd *cobra.Command, args []string) { + cmd.HelpFunc()(cmd, args) + }, + } + + memfaultEx := " " + nmutil.ToolInfo.ExeName + + " -c olimex memfault pull\n" + + memfaultDownloadCmd := &cobra.Command{ + Use: "pull -c ", + Short: "Pull memfault data from a device", + Example: memfaultEx, + Run: memfaultDownloadCmd, + } + memfaultCmd.AddCommand(memfaultDownloadCmd) + + return memfaultCmd +} diff --git a/nmxact/nmp/decode.go b/nmxact/nmp/decode.go index 4cbd095e..51baa9df 100644 --- a/nmxact/nmp/decode.go +++ b/nmxact/nmp/decode.go @@ -37,6 +37,7 @@ const gr_cra = NMP_GROUP_CRASH const gr_run = NMP_GROUP_RUN const gr_fil = NMP_GROUP_FS const gr_she = NMP_GROUP_SHELL +const gr_mft = NMP_GROUP_MEMFAULT // Op-Group-Id type Ogi struct { @@ -74,6 +75,7 @@ func fsUploadRspCtor() NmpRsp { return NewFsUploadRsp() } func configReadRspCtor() NmpRsp { return NewConfigReadRsp() } func configWriteRspCtor() NmpRsp { return NewConfigWriteRsp() } func shellExecRspCtor() NmpRsp { return NewShellExecRsp() } +func memfaultPullRspCtor() NmpRsp { return NewMemfaultPullRsp() } var rspCtorMap = map[Ogi]rspCtor{ {op_wr, gr_def, NMP_ID_DEF_ECHO}: echoRspCtor, @@ -104,6 +106,7 @@ var rspCtorMap = map[Ogi]rspCtor{ {op_rr, gr_cfg, NMP_ID_CONFIG_VAL}: configReadRspCtor, {op_wr, gr_cfg, NMP_ID_CONFIG_VAL}: configWriteRspCtor, {op_wr, gr_she, NMP_ID_SHELL_EXEC}: shellExecRspCtor, + {op_rr, gr_mft, NMP_ID_MEMFAULT_PULL}: memfaultPullRspCtor, } func DecodeRspBody(hdr *NmpHdr, body []byte) (NmpRsp, error) { diff --git a/nmxact/nmp/defs.go b/nmxact/nmp/defs.go index 5ef2c298..a4e11d6f 100644 --- a/nmxact/nmp/defs.go +++ b/nmxact/nmp/defs.go @@ -39,17 +39,18 @@ const ( // Per-user commands are then defined after group 64. const ( - NMP_GROUP_DEFAULT = 0 - NMP_GROUP_IMAGE = 1 - NMP_GROUP_STAT = 2 - NMP_GROUP_CONFIG = 3 - NMP_GROUP_LOG = 4 - NMP_GROUP_CRASH = 5 - NMP_GROUP_SPLIT = 6 - NMP_GROUP_RUN = 7 - NMP_GROUP_FS = 8 - NMP_GROUP_SHELL = 9 - NMP_GROUP_PERUSER = 64 + NMP_GROUP_DEFAULT = 0 + NMP_GROUP_IMAGE = 1 + NMP_GROUP_STAT = 2 + NMP_GROUP_CONFIG = 3 + NMP_GROUP_LOG = 4 + NMP_GROUP_CRASH = 5 + NMP_GROUP_SPLIT = 6 + NMP_GROUP_RUN = 7 + NMP_GROUP_FS = 8 + NMP_GROUP_SHELL = 9 + NMP_GROUP_MEMFAULT = 10 + NMP_GROUP_PERUSER = 64 ) // Default group (0). @@ -112,3 +113,8 @@ const ( const ( NMP_ID_SHELL_EXEC = 0 ) + +// Memfault group (8). +const ( + NMP_ID_MEMFAULT_PULL = 0 +) diff --git a/nmxact/nmp/memfault.go b/nmxact/nmp/memfault.go new file mode 100644 index 00000000..9e4e663e --- /dev/null +++ b/nmxact/nmp/memfault.go @@ -0,0 +1,49 @@ +/** + * Licensed to the 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. The 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 nmp + +////////////////////////////////////////////////////////////////////////////// +// $memfaultpull // +////////////////////////////////////////////////////////////////////////////// + +type MemfaultPullReq struct { + NmpBase `codec:"-"` +} + +type MemfaultPullRsp struct { + NmpBase + Rc int `codec:"rc"` + Status int `codec:"status"` + Data []byte `codec:"data"` +} + +func NewMemfaultPullReq() *MemfaultPullReq { + r := &MemfaultPullReq{} + fillNmpReq(r, NMP_OP_READ, NMP_GROUP_MEMFAULT, NMP_ID_MEMFAULT_PULL) + return r +} + +func (r *MemfaultPullReq) Msg() *NmpMsg { return MsgFromReq(r) } + +func NewMemfaultPullRsp() *MemfaultPullRsp { + return &MemfaultPullRsp{} +} + +func (r *MemfaultPullRsp) Msg() *NmpMsg { return MsgFromReq(r) } diff --git a/nmxact/xact/memfault.go b/nmxact/xact/memfault.go new file mode 100644 index 00000000..3a1626eb --- /dev/null +++ b/nmxact/xact/memfault.go @@ -0,0 +1,88 @@ +/** + * Licensed to the 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. The 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 xact + +import ( + "mynewt.apache.org/newtmgr/nmxact/nmp" + "mynewt.apache.org/newtmgr/nmxact/sesn" +) + +////////////////////////////////////////////////////////////////////////////// +// $memfaultpull // +////////////////////////////////////////////////////////////////////////////// + +const ( + MEMFAULT_PACKETIZER_STATUS_NO_MORE_DATA = 0 + MEMFAULT_PACKETIZER_STATUS_END_OF_CHUNK = 1 + MEMFAULT_PACKETIZER_STATUS_MORE_DATA_FOR_CHUNK = 2 +) + +type MemfaultPullProgressFn func(c *MemfaultPullCmd, r *nmp.MemfaultPullRsp) +type MemfaultPullCmd struct { + CmdBase + ProgressCb MemfaultPullProgressFn +} + +type MemfaultPullResult struct { + Rsps []*nmp.MemfaultPullRsp +} + +func NewMemfaultPullCmd() *MemfaultPullCmd { + return &MemfaultPullCmd{ + CmdBase: NewCmdBase(), + } +} + +func newMemfaultPullResult() *MemfaultPullResult { + return &MemfaultPullResult{} +} + +func (r *MemfaultPullResult) Status() int { + rsp := r.Rsps[len(r.Rsps)-1] + return rsp.Rc +} + +func (c *MemfaultPullCmd) Run(s sesn.Sesn) (Result, error) { + res := newMemfaultPullResult() + + for { + r := nmp.NewMemfaultPullReq() + + rsp, err := txReq(s, r.Msg(), &c.CmdBase) + if err != nil { + return nil, err + } + irsp := rsp.(*nmp.MemfaultPullRsp) + + if c.ProgressCb != nil { + c.ProgressCb(c, irsp) + } + + res.Rsps = append(res.Rsps, irsp) + if irsp.Status == MEMFAULT_PACKETIZER_STATUS_NO_MORE_DATA || + irsp.Status == MEMFAULT_PACKETIZER_STATUS_END_OF_CHUNK || + len(irsp.Data) == 0 { + // Download complete. + break + } + } + + return res, nil +}