-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Expand file tree
/
Copy pathdebug.go
More file actions
176 lines (150 loc) · 4.19 KB
/
debug.go
File metadata and controls
176 lines (150 loc) · 4.19 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
package frameworks
import (
"fmt"
"os"
"strconv"
"github.com/cloudfoundry/java-buildpack/src/java/common"
)
// DebugFramework implements Java remote debugging support
// Enables JDWP (Java Debug Wire Protocol) for remote debugging
type DebugFramework struct {
context *common.Context
}
// NewDebugFramework creates a new Debug framework instance
func NewDebugFramework(ctx *common.Context) *DebugFramework {
return &DebugFramework{context: ctx}
}
// Detect checks if debugging should be enabled
func (d *DebugFramework) Detect() (string, error) {
// Check if debug is enabled in configuration
config, err := d.loadConfig()
if err != nil {
d.context.Log.Warning("Failed to load debug config: %s", err.Error())
return "", nil // Don't fail the build
}
if !config.isEnabled() {
return "", nil
}
return "Remote Debug", nil
}
// Supply performs debug setup during supply phase
func (d *DebugFramework) Supply() error {
config, err := d.loadConfig()
if err != nil {
d.context.Log.Warning("Failed to load debug config: %s", err.Error())
return nil // Don't fail the build
}
if !config.isEnabled() {
return nil
}
port := config.getPort()
suspend := config.getSuspend()
suspendMsg := ""
if suspend {
suspendMsg = ", suspended on start"
}
d.context.Log.BeginStep("Debugging enabled on port %d%s", port, suspendMsg)
return nil
}
// Finalize adds debug options to JAVA_OPTS
func (d *DebugFramework) Finalize() error {
config, err := d.loadConfig()
if err != nil {
d.context.Log.Warning("Failed to load debug config: %s", err.Error())
return nil // Don't fail the build
}
if !config.isEnabled() {
return nil
}
port := config.getPort()
suspend := config.getSuspend()
// Build JDWP agent string
suspendValue := "n"
if suspend {
suspendValue = "y"
}
debugOpts := fmt.Sprintf("-agentlib:jdwp=transport=dt_socket,server=y,address=%d,suspend=%s", port, suspendValue)
// Write JAVA_OPTS to .opts file with priority 20 (Ruby buildpack line 54)
if err := writeJavaOptsFile(d.context, 20, "debug", debugOpts); err != nil {
return fmt.Errorf("failed to write java_opts file: %w", err)
}
return nil
}
// isEnabled checks if debugging is enabled
func (d *debugConfig) isEnabled() bool {
// Check BPL_DEBUG_ENABLED first (Cloud Native Buildpacks convention)
bplEnabled := os.Getenv("BPL_DEBUG_ENABLED")
if bplEnabled == "true" || bplEnabled == "1" {
return true
}
if bplEnabled == "false" || bplEnabled == "0" {
return false
}
return d.Enabled
}
// getPort returns the debug port
func (d *debugConfig) getPort() int {
// Check BPL_DEBUG_PORT first (Cloud Native Buildpacks convention)
bplPort := os.Getenv("BPL_DEBUG_PORT")
if bplPort != "" {
if port, err := strconv.Atoi(bplPort); err == nil && port > 0 {
return port
}
}
return d.Port
}
// getSuspend returns whether to suspend on start
func (d *debugConfig) getSuspend() bool {
return d.Suspend
}
type debugConfig struct {
Enabled bool `yaml:"enabled"`
Port int `yaml:"port"`
Suspend bool `yaml:"suspend"`
}
func (d *DebugFramework) loadConfig() (*debugConfig, error) {
// initialize default values
dbgConfig := debugConfig{
Enabled: false,
Port: 8000,
Suspend: false,
}
config := os.Getenv("JBP_CONFIG_DEBUG")
if config != "" {
yamlHandler := common.YamlHandler{}
err := yamlHandler.ValidateFields([]byte(config), &dbgConfig)
if err != nil {
d.context.Log.Warning("Unknown user config values: %s", err.Error())
}
// overlay JBP_CONFIG_DEBUG over default values
if err = yamlHandler.Unmarshal([]byte(config), &dbgConfig); err != nil {
return nil, fmt.Errorf("failed to parse JBP_CONFIG_DEBUG: %w", err)
}
}
return &dbgConfig, nil
}
// Helper function to check if string contains substring
func contains(s, substr string) bool {
return findInString(s, substr) != -1
}
// Helper function to find substring in string
func findInString(s, substr string) int {
for i := 0; i <= len(s)-len(substr); i++ {
if s[i:i+len(substr)] == substr {
return i
}
}
return -1
}
// Helper function to extract number from string
func extractNumber(s string) string {
num := ""
for i := 0; i < len(s); i++ {
if s[i] >= '0' && s[i] <= '9' {
num += string(s[i])
} else if num != "" {
break
}
}
return num
}