Add init debugging support
This adds Delve into the initramfs and a conditional hook which attaches Delve to our init
after the network is up. This allows for breakpoint-debugging the init itself, at least after the
very early node bringup.
Test Plan:
`bazel run -c dbg //:launch`, then use IDEA's Go Remote target to connect to localhost:2345
and set a breakpoint.
Bug: T786
X-Origin-Diff: phab/D581
GitOrigin-RevId: f6b32e7b7f4d36c8492df3e11ee97588817dbd8e
diff --git a/core/BUILD b/core/BUILD
index fcfc049..23cb537 100644
--- a/core/BUILD
+++ b/core/BUILD
@@ -1,5 +1,14 @@
load("//core/build:def.bzl", "smalltown_initramfs")
+# debug_build checks if we're building in debug mode and enables various debug features for the image. Currently this
+# is only used for attaching a Delve debugger to init when it's enabled.
+config_setting(
+ name = "debug_build",
+ values = {
+ "compilation_mode": "dbg",
+ },
+)
+
smalltown_initramfs(
name = "initramfs",
extra_dirs = [
@@ -39,6 +48,9 @@
"@com_github_cilium_cilium//cilium": "/cilium/bin/cilium",
"@com_github_cilium_cilium//daemon": "/cilium/bin/daemon",
"@com_github_cilium_cilium//operator": "/cilium/bin/operator",
+
+ # Delve
+ "@com_github_go_delve_delve//cmd/dlv:dlv": "/dlv",
},
)
diff --git a/core/cmd/init/BUILD.bazel b/core/cmd/init/BUILD.bazel
index 1e5db46..af37ea6 100644
--- a/core/cmd/init/BUILD.bazel
+++ b/core/cmd/init/BUILD.bazel
@@ -2,13 +2,18 @@
go_library(
name = "go_default_library",
+ # keep
srcs = [
"main.go",
"switchroot.go",
- ],
+ ] + select({
+ "//core:debug_build": ["debug_enabled.go"],
+ "//conditions:default": ["debug_disabled.go"],
+ }),
importpath = "git.monogon.dev/source/nexantic.git/core/cmd/init",
visibility = ["//visibility:private"],
deps = [
+ "//core/internal/common:go_default_library", # keep
"//core/internal/common/supervisor:go_default_library",
"//core/internal/network:go_default_library",
"//core/internal/node:go_default_library",
diff --git a/core/cmd/init/debug_disabled.go b/core/cmd/init/debug_disabled.go
new file mode 100644
index 0000000..06bcad0
--- /dev/null
+++ b/core/cmd/init/debug_disabled.go
@@ -0,0 +1,23 @@
+// Copyright 2020 The Monogon Project Authors.
+//
+// SPDX-License-Identifier: Apache-2.0
+//
+// Licensed 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 main
+
+import "git.monogon.dev/source/nexantic.git/core/internal/network"
+
+// initializeDebugger does nothing in a non-debug build
+func initializeDebugger(*network.Service) {
+}
diff --git a/core/cmd/init/debug_enabled.go b/core/cmd/init/debug_enabled.go
new file mode 100644
index 0000000..1c2af00
--- /dev/null
+++ b/core/cmd/init/debug_enabled.go
@@ -0,0 +1,42 @@
+// Copyright 2020 The Monogon Project Authors.
+//
+// SPDX-License-Identifier: Apache-2.0
+//
+// Licensed 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 main
+
+import (
+ "context"
+ "fmt"
+ "os/exec"
+
+ "git.monogon.dev/source/nexantic.git/core/internal/common"
+ "git.monogon.dev/source/nexantic.git/core/internal/network"
+)
+
+// initializeDebugger attaches Delve to ourselves and exposes it on common.DebuggerPort
+// This is coupled to compilation_mode=dbg because otherwise Delve doesn't have the necessary DWARF debug info
+func initializeDebugger(networkSvc *network.Service) {
+ go func() {
+ // This is intentionally delayed until network becomes available since Delve for some reason connects to itself
+ // and in early-boot no network interface is available to do that through. Also external access isn't possible
+ // early on anyways.
+ networkSvc.GetIP(context.Background(), true)
+ dlvCmd := exec.Command("/dlv", "--headless=true", fmt.Sprintf("--listen=:%v", common.DebuggerPort),
+ "--accept-multiclient", "--only-same-user=false", "attach", "--continue", "1", "/init")
+ if err := dlvCmd.Start(); err != nil {
+ panic(err)
+ }
+ }()
+}
diff --git a/core/cmd/init/main.go b/core/cmd/init/main.go
index 4fc949d..f5b09f8 100644
--- a/core/cmd/init/main.go
+++ b/core/cmd/init/main.go
@@ -86,6 +86,9 @@
panic(err)
}
+ // This function initializes a headless Delve if this is a debug build or does nothing if it's not
+ initializeDebugger(networkSvc)
+
supervisor.New(context.Background(), logger, func(ctx context.Context) error {
if err := supervisor.Run(ctx, "network", networkSvc.Run); err != nil {
return err
diff --git a/core/cmd/launch/main.go b/core/cmd/launch/main.go
index ff5c4d5..9bb4732 100644
--- a/core/cmd/launch/main.go
+++ b/core/cmd/launch/main.go
@@ -34,7 +34,7 @@
<-sigs
cancel()
}()
- if err := launch.Launch(ctx, launch.Options{Ports: launch.IdentityPortMap()}); err != nil {
+ if err := launch.Launch(ctx, launch.Options{Ports: launch.IdentityPortMap(), SerialPort: os.Stdout}); err != nil {
if err == ctx.Err() {
return
}
diff --git a/core/internal/common/setup.go b/core/internal/common/setup.go
index 531b688..fa5cd59 100644
--- a/core/internal/common/setup.go
+++ b/core/internal/common/setup.go
@@ -35,6 +35,7 @@
ExternalServicePort = 7836
DebugServicePort = 7837
KubernetesAPIPort = 6443
+ DebuggerPort = 2345
)
const (
diff --git a/core/internal/launch/launch.go b/core/internal/launch/launch.go
index 8aa865f..774b432 100644
--- a/core/internal/launch/launch.go
+++ b/core/internal/launch/launch.go
@@ -148,7 +148,7 @@
}
var requiredPorts = []uint16{common.ConsensusPort, common.NodeServicePort, common.MasterServicePort,
- common.ExternalServicePort, common.DebugServicePort, common.KubernetesAPIPort}
+ common.ExternalServicePort, common.DebugServicePort, common.KubernetesAPIPort, common.DebuggerPort}
// IdentityPortMap returns a port map where each VM port is mapped onto itself on the host. This is mainly useful
// for development against Smalltown. The dbg command requires this mapping.