or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

atomic-arrays.mdatomic-operations.mdindex.mdlocks.mdthread-parking.mdtracing.md

tracing.mddocs/

0

# Tracing and Debugging

1

2

Comprehensive tracing system for monitoring and debugging atomic operations in concurrent code, providing detailed operation logs and custom formatting support.

3

4

## Capabilities

5

6

### Trace Factory Function

7

8

Creates trace objects for monitoring atomic operations with configurable size and formatting.

9

10

```kotlin { .api }

11

/**

12

* Creates Trace object for tracing atomic operations.

13

* @param size - Size of the circular trace buffer (default: 32)

14

* @param format - Custom trace formatter (default: traceFormatDefault)

15

* @returns TraceBase instance for tracing operations

16

*/

17

fun Trace(size: Int = 32, format: TraceFormat = traceFormatDefault): TraceBase

18

```

19

20

**Usage Examples:**

21

22

```kotlin

23

import kotlinx.atomicfu.*

24

25

class TracedCounter {

26

private val trace = Trace(64) // Larger buffer for more history

27

private val count = atomic(0, trace.named("count"))

28

29

fun increment(): Int {

30

trace { "Incrementing counter" }

31

return count.incrementAndGet()

32

}

33

34

fun add(delta: Int): Int {

35

trace { "Adding $delta to counter" }

36

return count.addAndGet(delta)

37

}

38

39

fun getTraceHistory(): String = trace.toString()

40

}

41

42

// Usage

43

val counter = TracedCounter()

44

counter.increment()

45

counter.add(5)

46

println(counter.getTraceHistory())

47

```

48

49

### Named Tracing

50

51

Adds meaningful names to traces for better debugging context.

52

53

```kotlin { .api }

54

/**

55

* Adds a name to the trace for better identification.

56

* @param name - Name prefix for all trace messages

57

* @returns Named TraceBase instance

58

*/

59

fun TraceBase.named(name: String): TraceBase

60

```

61

62

**Usage Examples:**

63

64

```kotlin

65

import kotlinx.atomicfu.*

66

67

class ConnectionPool {

68

private val trace = Trace(128)

69

private val activeConnections = atomic(0, trace.named("active"))

70

private val totalRequests = atomic(0L, trace.named("requests"))

71

72

fun borrowConnection(): Boolean {

73

totalRequests.incrementAndGet()

74

75

val current = activeConnections.value

76

if (current < 10) { // Max 10 connections

77

return activeConnections.compareAndSet(current, current + 1)

78

}

79

80

trace { "Connection pool exhausted, current: $current" }

81

return false

82

}

83

84

fun returnConnection() {

85

activeConnections.decrementAndGet()

86

trace { "Connection returned to pool" }

87

}

88

89

fun printTraceHistory() {

90

println("Connection Pool Trace:")

91

println(trace.toString())

92

}

93

}

94

```

95

96

### TraceBase Class

97

98

Base class for all trace implementations providing multiple append methods.

99

100

```kotlin { .api }

101

open class TraceBase {

102

/** Appends single event to the trace */

103

open fun append(event: Any)

104

105

/** Appends two events to the trace */

106

open fun append(event1: Any, event2: Any)

107

108

/** Appends three events to the trace */

109

open fun append(event1: Any, event2: Any, event3: Any)

110

111

/** Appends four events to the trace */

112

open fun append(event1: Any, event2: Any, event3: Any, event4: Any)

113

114

/** Functional style event appending */

115

inline operator fun invoke(event: () -> Any)

116

117

/** NOP tracing singleton */

118

object None : TraceBase()

119

}

120

```

121

122

**Usage Examples:**

123

124

```kotlin

125

import kotlinx.atomicfu.*

126

127

class MessageProcessor {

128

private val trace = Trace(256)

129

private val messagesProcessed = atomic(0L)

130

private val errors = atomic(0)

131

132

fun processMessage(messageId: String, content: String) {

133

val startTime = System.currentTimeMillis()

134

135

try {

136

// Multi-append for garbage-free logging

137

trace.append("Processing message", messageId, content.length, Thread.currentThread())

138

139

// Simulate processing

140

Thread.sleep(10)

141

142

messagesProcessed.incrementAndGet()

143

val endTime = System.currentTimeMillis()

144

145

trace.append("Message processed", messageId, endTime - startTime)

146

147

} catch (e: Exception) {

148

errors.incrementAndGet()

149

trace.append("Message failed", messageId, e.message ?: "Unknown error")

150

}

151

}

152

153

fun getStats(): String {

154

return "Processed: ${messagesProcessed.value}, Errors: ${errors.value}"

155

}

156

157

fun getTrace(): String = trace.toString()

158

}

159

```

160

161

### TraceFormat Class

162

163

Custom trace formatting for specialized logging requirements.

164

165

```kotlin { .api }

166

/**

167

* Trace string formatter for customizing trace output.

168

*/

169

open class TraceFormat {

170

/**

171

* Formats trace entry with index and event information.

172

* @param index - Sequential index of the trace entry

173

* @param event - Event object to format

174

* @returns Formatted string representation

175

*/

176

open fun format(index: Int, event: Any): String

177

}

178

179

/**

180

* Creates trace string formatter with custom format function.

181

* @param format - Lambda function for custom formatting

182

* @returns TraceFormat instance with custom formatting

183

*/

184

inline fun TraceFormat(crossinline format: (index: Int, event: Any) -> String): TraceFormat

185

```

186

187

**Usage Examples:**

188

189

```kotlin

190

import kotlinx.atomicfu.*

191

192

class CustomTracedService {

193

// Custom formatter with timestamp and thread info

194

private val customFormat = TraceFormat { index, event ->

195

val timestamp = System.currentTimeMillis()

196

val threadName = Thread.currentThread().name

197

"[$timestamp][$threadName][$index] $event"

198

}

199

200

private val trace = Trace(100, customFormat)

201

private val state = atomic("IDLE", trace.named("state"))

202

203

fun start() {

204

trace { "Service starting" }

205

state.value = "STARTING"

206

207

// Simulate startup work

208

Thread.sleep(100)

209

210

state.value = "RUNNING"

211

trace { "Service started successfully" }

212

}

213

214

fun stop() {

215

trace { "Service stopping" }

216

state.value = "STOPPING"

217

218

// Simulate cleanup work

219

Thread.sleep(50)

220

221

state.value = "STOPPED"

222

trace { "Service stopped" }

223

}

224

225

fun getDetailedTrace(): String = trace.toString()

226

}

227

```

228

229

### Default Trace Format

230

231

Pre-configured trace formatters with optional thread information.

232

233

```kotlin { .api }

234

/**

235

* The default trace string formatter.

236

* On JVM, when 'kotlinx.atomicfu.trace.thread' system property is set,

237

* the default format also includes thread name for each operation.

238

*/

239

val traceFormatDefault: TraceFormat

240

```

241

242

**Usage Examples:**

243

244

```kotlin

245

import kotlinx.atomicfu.*

246

247

// Enable thread names in trace (set system property)

248

// -Dkotlinx.atomicfu.trace.thread=true

249

250

class ThreadAwareService {

251

private val trace = Trace() // Uses traceFormatDefault

252

private val workItems = atomic(0, trace.named("work"))

253

254

fun doWork() {

255

trace { "Starting work on ${Thread.currentThread().name}" }

256

workItems.incrementAndGet()

257

258

// Simulate work

259

Thread.sleep(50)

260

261

trace { "Work completed" }

262

}

263

264

fun printTrace() {

265

println("Service Trace (with thread info if enabled):")

266

println(trace.toString())

267

}

268

}

269

270

// Usage with multiple threads

271

val service = ThreadAwareService()

272

273

// Create multiple worker threads

274

repeat(3) { threadId ->

275

Thread {

276

repeat(2) {

277

service.doWork()

278

}

279

}.apply {

280

name = "Worker-$threadId"

281

start()

282

}

283

}

284

```

285

286

## Advanced Tracing Patterns

287

288

### Conditional Tracing

289

290

Tracing that can be enabled/disabled at runtime:

291

292

```kotlin

293

import kotlinx.atomicfu.*

294

295

class ConditionalTracer {

296

private val debugEnabled = atomic(false)

297

private val trace = Trace(512)

298

private val operations = atomic(0L)

299

300

fun enableDebug() = debugEnabled.compareAndSet(false, true)

301

fun disableDebug() = debugEnabled.compareAndSet(true, false)

302

303

private fun debugTrace(event: () -> Any) {

304

if (debugEnabled.value) {

305

trace(event)

306

}

307

}

308

309

fun performOperation(operationName: String) {

310

debugTrace { "Starting operation: $operationName" }

311

312

operations.incrementAndGet()

313

314

// Simulate work

315

Thread.sleep(10)

316

317

debugTrace { "Completed operation: $operationName" }

318

}

319

320

fun getTrace(): String = if (debugEnabled.value) trace.toString() else "Tracing disabled"

321

}

322

```

323

324

### Performance Monitoring

325

326

Using traces for performance analysis:

327

328

```kotlin

329

import kotlinx.atomicfu.*

330

331

class PerformanceTracer {

332

private val performanceFormat = TraceFormat { index, event ->

333

val memoryUsage = Runtime.getRuntime().let { rt ->

334

(rt.totalMemory() - rt.freeMemory()) / 1024 / 1024

335

}

336

"[$index][${System.nanoTime()}][${memoryUsage}MB] $event"

337

}

338

339

private val trace = Trace(1000, performanceFormat)

340

private val operationCount = atomic(0L, trace.named("ops"))

341

342

fun timedOperation(name: String, block: () -> Unit) {

343

val startTime = System.nanoTime()

344

trace { "START: $name" }

345

346

try {

347

block()

348

operationCount.incrementAndGet()

349

} finally {

350

val duration = (System.nanoTime() - startTime) / 1_000_000 // Convert to ms

351

trace { "END: $name (${duration}ms)" }

352

}

353

}

354

355

fun getPerformanceReport(): String = trace.toString()

356

}

357

```

358

359

## Implementation Notes

360

361

### Trace Buffer Management

362

363

- Traces use circular buffers with configurable size

364

- Older entries are overwritten when buffer is full

365

- Buffer size should be power of 2 for optimal performance

366

- Larger buffers provide more history but use more memory

367

368

### Thread Safety

369

370

- All trace operations are thread-safe

371

- Multiple threads can append to the same trace concurrently

372

- Trace formatting is applied during toString() call

373

- No synchronization overhead during trace appending

374

375

### Performance Considerations

376

377

- Trace operations have minimal overhead when tracing is disabled (TraceBase.None)

378

- Lambda-based tracing (`trace { ... }`) only evaluates when tracing is enabled

379

- Multi-append methods are garbage-free for performance-critical code

380

- Custom formatters are only called during trace output, not during appending