File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -13,6 +13,7 @@ import { Flag } from "@/flag/flag"
1313import { setTimeout as sleep } from "node:timers/promises"
1414import { writeHeapSnapshot } from "node:v8"
1515import { WorkspaceID } from "@/control-plane/schema"
16+ import { Heap } from "@/cli/heap"
1617
1718await Log . init ( {
1819 print : process . argv . includes ( "--print-logs" ) ,
@@ -23,6 +24,8 @@ await Log.init({
2324 } ) ( ) ,
2425} )
2526
27+ Heap . start ( )
28+
2629process . on ( "unhandledRejection" , ( e ) => {
2730 Log . Default . error ( "rejection" , {
2831 e : e instanceof Error ? e . message : e ,
Original file line number Diff line number Diff line change 1+ import path from "path"
2+ import { writeHeapSnapshot } from "node:v8"
3+ import { Flag } from "@/flag/flag"
4+ import { Global } from "@/global"
5+ import { Log } from "@/util/log"
6+
7+ const log = Log . create ( { service : "heap" } )
8+ const MINUTE = 60_000
9+ const LIMIT = 2 * 1024 * 1024 * 1024
10+
11+ export namespace Heap {
12+ let timer : Timer | undefined
13+ let lock = false
14+ let armed = true
15+
16+ export function start ( ) {
17+ if ( ! Flag . OPENCODE_AUTO_HEAP_SNAPSHOT ) return
18+ if ( timer ) return
19+
20+ const run = async ( ) => {
21+ if ( lock ) return
22+
23+ const stat = process . memoryUsage ( )
24+ if ( stat . rss <= LIMIT ) {
25+ armed = true
26+ return
27+ }
28+ if ( ! armed ) return
29+
30+ lock = true
31+ armed = false
32+ const file = path . join (
33+ Global . Path . log ,
34+ `heap-${ process . pid } -${ new Date ( ) . toISOString ( ) . replace ( / [: .] / g, "" ) } .heapsnapshot` ,
35+ )
36+ log . warn ( "heap usage exceeded limit" , {
37+ rss : stat . rss ,
38+ heap : stat . heapUsed ,
39+ file,
40+ } )
41+
42+ await Promise . resolve ( )
43+ . then ( ( ) => writeHeapSnapshot ( file ) )
44+ . catch ( ( err ) => {
45+ log . error ( "failed to write heap snapshot" , {
46+ error : err instanceof Error ? err . message : String ( err ) ,
47+ file,
48+ } )
49+ } )
50+
51+ lock = false
52+ }
53+
54+ timer = setInterval ( ( ) => {
55+ void run ( )
56+ } , MINUTE )
57+ timer . unref ?.( )
58+ }
59+ }
Original file line number Diff line number Diff line change @@ -12,6 +12,7 @@ function falsy(key: string) {
1212
1313export namespace Flag {
1414 export const OPENCODE_AUTO_SHARE = truthy ( "OPENCODE_AUTO_SHARE" )
15+ export const OPENCODE_AUTO_HEAP_SNAPSHOT = truthy ( "OPENCODE_AUTO_HEAP_SNAPSHOT" )
1516 export const OPENCODE_GIT_BASH_PATH = process . env [ "OPENCODE_GIT_BASH_PATH" ]
1617 export const OPENCODE_CONFIG = process . env [ "OPENCODE_CONFIG" ]
1718 export declare const OPENCODE_PURE : boolean
Original file line number Diff line number Diff line change @@ -35,6 +35,7 @@ import { JsonMigration } from "./storage/json-migration"
3535import { Database } from "./storage/db"
3636import { errorMessage } from "./util/error"
3737import { PluginCommand } from "./cli/cmd/plug"
38+ import { Heap } from "./cli/heap"
3839
3940process . on ( "unhandledRejection" , ( e ) => {
4041 Log . Default . error ( "rejection" , {
@@ -96,6 +97,8 @@ const cli = yargs(args)
9697 } ) ( ) ,
9798 } )
9899
100+ Heap . start ( )
101+
99102 process . env . AGENT = "1"
100103 process . env . OPENCODE = "1"
101104 process . env . OPENCODE_PID = String ( process . pid )
You can’t perform that action at this time.
0 commit comments