github.com/m10x/go/src@v0.0.0-20220112094212-ba61592315da/runtime/os_linux.go (about) 1 // Copyright 2009 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package runtime 6 7 import ( 8 "internal/abi" 9 "internal/goarch" 10 "runtime/internal/atomic" 11 "unsafe" 12 ) 13 14 type mOS struct { 15 // profileTimer holds the ID of the POSIX interval timer for profiling CPU 16 // usage on this thread. 17 // 18 // It is valid when the profileTimerValid field is non-zero. A thread 19 // creates and manages its own timer, and these fields are read and written 20 // only by this thread. But because some of the reads on profileTimerValid 21 // are in signal handling code, access to that field uses atomic operations. 22 profileTimer int32 23 profileTimerValid uint32 24 } 25 26 //go:noescape 27 func futex(addr unsafe.Pointer, op int32, val uint32, ts, addr2 unsafe.Pointer, val3 uint32) int32 28 29 // Linux futex. 30 // 31 // futexsleep(uint32 *addr, uint32 val) 32 // futexwakeup(uint32 *addr) 33 // 34 // Futexsleep atomically checks if *addr == val and if so, sleeps on addr. 35 // Futexwakeup wakes up threads sleeping on addr. 36 // Futexsleep is allowed to wake up spuriously. 37 38 const ( 39 _FUTEX_PRIVATE_FLAG = 128 40 _FUTEX_WAIT_PRIVATE = 0 | _FUTEX_PRIVATE_FLAG 41 _FUTEX_WAKE_PRIVATE = 1 | _FUTEX_PRIVATE_FLAG 42 ) 43 44 // Atomically, 45 // if(*addr == val) sleep 46 // Might be woken up spuriously; that's allowed. 47 // Don't sleep longer than ns; ns < 0 means forever. 48 //go:nosplit 49 func futexsleep(addr *uint32, val uint32, ns int64) { 50 // Some Linux kernels have a bug where futex of 51 // FUTEX_WAIT returns an internal error code 52 // as an errno. Libpthread ignores the return value 53 // here, and so can we: as it says a few lines up, 54 // spurious wakeups are allowed. 55 if ns < 0 { 56 futex(unsafe.Pointer(addr), _FUTEX_WAIT_PRIVATE, val, nil, nil, 0) 57 return 58 } 59 60 var ts timespec 61 ts.setNsec(ns) 62 futex(unsafe.Pointer(addr), _FUTEX_WAIT_PRIVATE, val, unsafe.Pointer(&ts), nil, 0) 63 } 64 65 // If any procs are sleeping on addr, wake up at most cnt. 66 //go:nosplit 67 func futexwakeup(addr *uint32, cnt uint32) { 68 ret := futex(unsafe.Pointer(addr), _FUTEX_WAKE_PRIVATE, cnt, nil, nil, 0) 69 if ret >= 0 { 70 return 71 } 72 73 // I don't know that futex wakeup can return 74 // EAGAIN or EINTR, but if it does, it would be 75 // safe to loop and call futex again. 76 systemstack(func() { 77 print("futexwakeup addr=", addr, " returned ", ret, "\n") 78 }) 79 80 *(*int32)(unsafe.Pointer(uintptr(0x1006))) = 0x1006 81 } 82 83 func getproccount() int32 { 84 // This buffer is huge (8 kB) but we are on the system stack 85 // and there should be plenty of space (64 kB). 86 // Also this is a leaf, so we're not holding up the memory for long. 87 // See golang.org/issue/11823. 88 // The suggested behavior here is to keep trying with ever-larger 89 // buffers, but we don't have a dynamic memory allocator at the 90 // moment, so that's a bit tricky and seems like overkill. 91 const maxCPUs = 64 * 1024 92 var buf [maxCPUs / 8]byte 93 r := sched_getaffinity(0, unsafe.Sizeof(buf), &buf[0]) 94 if r < 0 { 95 return 1 96 } 97 n := int32(0) 98 for _, v := range buf[:r] { 99 for v != 0 { 100 n += int32(v & 1) 101 v >>= 1 102 } 103 } 104 if n == 0 { 105 n = 1 106 } 107 return n 108 } 109 110 // Clone, the Linux rfork. 111 const ( 112 _CLONE_VM = 0x100 113 _CLONE_FS = 0x200 114 _CLONE_FILES = 0x400 115 _CLONE_SIGHAND = 0x800 116 _CLONE_PTRACE = 0x2000 117 _CLONE_VFORK = 0x4000 118 _CLONE_PARENT = 0x8000 119 _CLONE_THREAD = 0x10000 120 _CLONE_NEWNS = 0x20000 121 _CLONE_SYSVSEM = 0x40000 122 _CLONE_SETTLS = 0x80000 123 _CLONE_PARENT_SETTID = 0x100000 124 _CLONE_CHILD_CLEARTID = 0x200000 125 _CLONE_UNTRACED = 0x800000 126 _CLONE_CHILD_SETTID = 0x1000000 127 _CLONE_STOPPED = 0x2000000 128 _CLONE_NEWUTS = 0x4000000 129 _CLONE_NEWIPC = 0x8000000 130 131 // As of QEMU 2.8.0 (5ea2fc84d), user emulation requires all six of these 132 // flags to be set when creating a thread; attempts to share the other 133 // five but leave SYSVSEM unshared will fail with -EINVAL. 134 // 135 // In non-QEMU environments CLONE_SYSVSEM is inconsequential as we do not 136 // use System V semaphores. 137 138 cloneFlags = _CLONE_VM | /* share memory */ 139 _CLONE_FS | /* share cwd, etc */ 140 _CLONE_FILES | /* share fd table */ 141 _CLONE_SIGHAND | /* share sig handler table */ 142 _CLONE_SYSVSEM | /* share SysV semaphore undo lists (see issue #20763) */ 143 _CLONE_THREAD /* revisit - okay for now */ 144 ) 145 146 //go:noescape 147 func clone(flags int32, stk, mp, gp, fn unsafe.Pointer) int32 148 149 // May run with m.p==nil, so write barriers are not allowed. 150 //go:nowritebarrier 151 func newosproc(mp *m) { 152 stk := unsafe.Pointer(mp.g0.stack.hi) 153 /* 154 * note: strace gets confused if we use CLONE_PTRACE here. 155 */ 156 if false { 157 print("newosproc stk=", stk, " m=", mp, " g=", mp.g0, " clone=", abi.FuncPCABI0(clone), " id=", mp.id, " ostk=", &mp, "\n") 158 } 159 160 // Disable signals during clone, so that the new thread starts 161 // with signals disabled. It will enable them in minit. 162 var oset sigset 163 sigprocmask(_SIG_SETMASK, &sigset_all, &oset) 164 ret := clone(cloneFlags, stk, unsafe.Pointer(mp), unsafe.Pointer(mp.g0), unsafe.Pointer(abi.FuncPCABI0(mstart))) 165 sigprocmask(_SIG_SETMASK, &oset, nil) 166 167 if ret < 0 { 168 print("runtime: failed to create new OS thread (have ", mcount(), " already; errno=", -ret, ")\n") 169 if ret == -_EAGAIN { 170 println("runtime: may need to increase max user processes (ulimit -u)") 171 } 172 throw("newosproc") 173 } 174 } 175 176 // Version of newosproc that doesn't require a valid G. 177 //go:nosplit 178 func newosproc0(stacksize uintptr, fn unsafe.Pointer) { 179 stack := sysAlloc(stacksize, &memstats.stacks_sys) 180 if stack == nil { 181 write(2, unsafe.Pointer(&failallocatestack[0]), int32(len(failallocatestack))) 182 exit(1) 183 } 184 ret := clone(cloneFlags, unsafe.Pointer(uintptr(stack)+stacksize), nil, nil, fn) 185 if ret < 0 { 186 write(2, unsafe.Pointer(&failthreadcreate[0]), int32(len(failthreadcreate))) 187 exit(1) 188 } 189 } 190 191 var failallocatestack = []byte("runtime: failed to allocate stack for the new OS thread\n") 192 var failthreadcreate = []byte("runtime: failed to create new OS thread\n") 193 194 const ( 195 _AT_NULL = 0 // End of vector 196 _AT_PAGESZ = 6 // System physical page size 197 _AT_HWCAP = 16 // hardware capability bit vector 198 _AT_RANDOM = 25 // introduced in 2.6.29 199 _AT_HWCAP2 = 26 // hardware capability bit vector 2 200 ) 201 202 var procAuxv = []byte("/proc/self/auxv\x00") 203 204 var addrspace_vec [1]byte 205 206 func mincore(addr unsafe.Pointer, n uintptr, dst *byte) int32 207 208 func sysargs(argc int32, argv **byte) { 209 n := argc + 1 210 211 // skip over argv, envp to get to auxv 212 for argv_index(argv, n) != nil { 213 n++ 214 } 215 216 // skip NULL separator 217 n++ 218 219 // now argv+n is auxv 220 auxv := (*[1 << 28]uintptr)(add(unsafe.Pointer(argv), uintptr(n)*goarch.PtrSize)) 221 if sysauxv(auxv[:]) != 0 { 222 return 223 } 224 // In some situations we don't get a loader-provided 225 // auxv, such as when loaded as a library on Android. 226 // Fall back to /proc/self/auxv. 227 fd := open(&procAuxv[0], 0 /* O_RDONLY */, 0) 228 if fd < 0 { 229 // On Android, /proc/self/auxv might be unreadable (issue 9229), so we fallback to 230 // try using mincore to detect the physical page size. 231 // mincore should return EINVAL when address is not a multiple of system page size. 232 const size = 256 << 10 // size of memory region to allocate 233 p, err := mmap(nil, size, _PROT_READ|_PROT_WRITE, _MAP_ANON|_MAP_PRIVATE, -1, 0) 234 if err != 0 { 235 return 236 } 237 var n uintptr 238 for n = 4 << 10; n < size; n <<= 1 { 239 err := mincore(unsafe.Pointer(uintptr(p)+n), 1, &addrspace_vec[0]) 240 if err == 0 { 241 physPageSize = n 242 break 243 } 244 } 245 if physPageSize == 0 { 246 physPageSize = size 247 } 248 munmap(p, size) 249 return 250 } 251 var buf [128]uintptr 252 n = read(fd, noescape(unsafe.Pointer(&buf[0])), int32(unsafe.Sizeof(buf))) 253 closefd(fd) 254 if n < 0 { 255 return 256 } 257 // Make sure buf is terminated, even if we didn't read 258 // the whole file. 259 buf[len(buf)-2] = _AT_NULL 260 sysauxv(buf[:]) 261 } 262 263 // startupRandomData holds random bytes initialized at startup. These come from 264 // the ELF AT_RANDOM auxiliary vector. 265 var startupRandomData []byte 266 267 func sysauxv(auxv []uintptr) int { 268 var i int 269 for ; auxv[i] != _AT_NULL; i += 2 { 270 tag, val := auxv[i], auxv[i+1] 271 switch tag { 272 case _AT_RANDOM: 273 // The kernel provides a pointer to 16-bytes 274 // worth of random data. 275 startupRandomData = (*[16]byte)(unsafe.Pointer(val))[:] 276 277 case _AT_PAGESZ: 278 physPageSize = val 279 } 280 281 archauxv(tag, val) 282 vdsoauxv(tag, val) 283 } 284 return i / 2 285 } 286 287 var sysTHPSizePath = []byte("/sys/kernel/mm/transparent_hugepage/hpage_pmd_size\x00") 288 289 func getHugePageSize() uintptr { 290 var numbuf [20]byte 291 fd := open(&sysTHPSizePath[0], 0 /* O_RDONLY */, 0) 292 if fd < 0 { 293 return 0 294 } 295 ptr := noescape(unsafe.Pointer(&numbuf[0])) 296 n := read(fd, ptr, int32(len(numbuf))) 297 closefd(fd) 298 if n <= 0 { 299 return 0 300 } 301 n-- // remove trailing newline 302 v, ok := atoi(slicebytetostringtmp((*byte)(ptr), int(n))) 303 if !ok || v < 0 { 304 v = 0 305 } 306 if v&(v-1) != 0 { 307 // v is not a power of 2 308 return 0 309 } 310 return uintptr(v) 311 } 312 313 func osinit() { 314 ncpu = getproccount() 315 physHugePageSize = getHugePageSize() 316 if iscgo { 317 // #42494 glibc and musl reserve some signals for 318 // internal use and require they not be blocked by 319 // the rest of a normal C runtime. When the go runtime 320 // blocks...unblocks signals, temporarily, the blocked 321 // interval of time is generally very short. As such, 322 // these expectations of *libc code are mostly met by 323 // the combined go+cgo system of threads. However, 324 // when go causes a thread to exit, via a return from 325 // mstart(), the combined runtime can deadlock if 326 // these signals are blocked. Thus, don't block these 327 // signals when exiting threads. 328 // - glibc: SIGCANCEL (32), SIGSETXID (33) 329 // - musl: SIGTIMER (32), SIGCANCEL (33), SIGSYNCCALL (34) 330 sigdelset(&sigsetAllExiting, 32) 331 sigdelset(&sigsetAllExiting, 33) 332 sigdelset(&sigsetAllExiting, 34) 333 } 334 osArchInit() 335 } 336 337 var urandom_dev = []byte("/dev/urandom\x00") 338 339 func getRandomData(r []byte) { 340 if startupRandomData != nil { 341 n := copy(r, startupRandomData) 342 extendRandom(r, n) 343 return 344 } 345 fd := open(&urandom_dev[0], 0 /* O_RDONLY */, 0) 346 n := read(fd, unsafe.Pointer(&r[0]), int32(len(r))) 347 closefd(fd) 348 extendRandom(r, int(n)) 349 } 350 351 func goenvs() { 352 goenvs_unix() 353 } 354 355 // Called to do synchronous initialization of Go code built with 356 // -buildmode=c-archive or -buildmode=c-shared. 357 // None of the Go runtime is initialized. 358 //go:nosplit 359 //go:nowritebarrierrec 360 func libpreinit() { 361 initsig(true) 362 } 363 364 // Called to initialize a new m (including the bootstrap m). 365 // Called on the parent thread (main thread in case of bootstrap), can allocate memory. 366 func mpreinit(mp *m) { 367 mp.gsignal = malg(32 * 1024) // Linux wants >= 2K 368 mp.gsignal.m = mp 369 } 370 371 func gettid() uint32 372 373 // Called to initialize a new m (including the bootstrap m). 374 // Called on the new thread, cannot allocate memory. 375 func minit() { 376 minitSignals() 377 378 // Cgo-created threads and the bootstrap m are missing a 379 // procid. We need this for asynchronous preemption and it's 380 // useful in debuggers. 381 getg().m.procid = uint64(gettid()) 382 } 383 384 // Called from dropm to undo the effect of an minit. 385 //go:nosplit 386 func unminit() { 387 unminitSignals() 388 } 389 390 // Called from exitm, but not from drop, to undo the effect of thread-owned 391 // resources in minit, semacreate, or elsewhere. Do not take locks after calling this. 392 func mdestroy(mp *m) { 393 } 394 395 //#ifdef GOARCH_386 396 //#define sa_handler k_sa_handler 397 //#endif 398 399 func sigreturn() 400 func sigtramp() // Called via C ABI 401 func cgoSigtramp() 402 403 //go:noescape 404 func sigaltstack(new, old *stackt) 405 406 //go:noescape 407 func setitimer(mode int32, new, old *itimerval) 408 409 //go:noescape 410 func timer_create(clockid int32, sevp *sigevent, timerid *int32) int32 411 412 //go:noescape 413 func timer_settime(timerid int32, flags int32, new, old *itimerspec) int32 414 415 //go:noescape 416 func timer_delete(timerid int32) int32 417 418 //go:noescape 419 func rtsigprocmask(how int32, new, old *sigset, size int32) 420 421 //go:nosplit 422 //go:nowritebarrierrec 423 func sigprocmask(how int32, new, old *sigset) { 424 rtsigprocmask(how, new, old, int32(unsafe.Sizeof(*new))) 425 } 426 427 func raise(sig uint32) 428 func raiseproc(sig uint32) 429 430 //go:noescape 431 func sched_getaffinity(pid, len uintptr, buf *byte) int32 432 func osyield() 433 434 //go:nosplit 435 func osyield_no_g() { 436 osyield() 437 } 438 439 func pipe() (r, w int32, errno int32) 440 func pipe2(flags int32) (r, w int32, errno int32) 441 func setNonblock(fd int32) 442 443 const ( 444 _si_max_size = 128 445 _sigev_max_size = 64 446 ) 447 448 //go:nosplit 449 //go:nowritebarrierrec 450 func setsig(i uint32, fn uintptr) { 451 var sa sigactiont 452 sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK | _SA_RESTORER | _SA_RESTART 453 sigfillset(&sa.sa_mask) 454 // Although Linux manpage says "sa_restorer element is obsolete and 455 // should not be used". x86_64 kernel requires it. Only use it on 456 // x86. 457 if GOARCH == "386" || GOARCH == "amd64" { 458 sa.sa_restorer = abi.FuncPCABI0(sigreturn) 459 } 460 if fn == abi.FuncPCABIInternal(sighandler) { // abi.FuncPCABIInternal(sighandler) matches the callers in signal_unix.go 461 if iscgo { 462 fn = abi.FuncPCABI0(cgoSigtramp) 463 } else { 464 fn = abi.FuncPCABI0(sigtramp) 465 } 466 } 467 sa.sa_handler = fn 468 sigaction(i, &sa, nil) 469 } 470 471 //go:nosplit 472 //go:nowritebarrierrec 473 func setsigstack(i uint32) { 474 var sa sigactiont 475 sigaction(i, nil, &sa) 476 if sa.sa_flags&_SA_ONSTACK != 0 { 477 return 478 } 479 sa.sa_flags |= _SA_ONSTACK 480 sigaction(i, &sa, nil) 481 } 482 483 //go:nosplit 484 //go:nowritebarrierrec 485 func getsig(i uint32) uintptr { 486 var sa sigactiont 487 sigaction(i, nil, &sa) 488 return sa.sa_handler 489 } 490 491 // setSignaltstackSP sets the ss_sp field of a stackt. 492 //go:nosplit 493 func setSignalstackSP(s *stackt, sp uintptr) { 494 *(*uintptr)(unsafe.Pointer(&s.ss_sp)) = sp 495 } 496 497 //go:nosplit 498 func (c *sigctxt) fixsigcode(sig uint32) { 499 } 500 501 // sysSigaction calls the rt_sigaction system call. 502 //go:nosplit 503 func sysSigaction(sig uint32, new, old *sigactiont) { 504 if rt_sigaction(uintptr(sig), new, old, unsafe.Sizeof(sigactiont{}.sa_mask)) != 0 { 505 // Workaround for bugs in QEMU user mode emulation. 506 // 507 // QEMU turns calls to the sigaction system call into 508 // calls to the C library sigaction call; the C 509 // library call rejects attempts to call sigaction for 510 // SIGCANCEL (32) or SIGSETXID (33). 511 // 512 // QEMU rejects calling sigaction on SIGRTMAX (64). 513 // 514 // Just ignore the error in these case. There isn't 515 // anything we can do about it anyhow. 516 if sig != 32 && sig != 33 && sig != 64 { 517 // Use system stack to avoid split stack overflow on ppc64/ppc64le. 518 systemstack(func() { 519 throw("sigaction failed") 520 }) 521 } 522 } 523 } 524 525 // rt_sigaction is implemented in assembly. 526 //go:noescape 527 func rt_sigaction(sig uintptr, new, old *sigactiont, size uintptr) int32 528 529 func getpid() int 530 func tgkill(tgid, tid, sig int) 531 532 // signalM sends a signal to mp. 533 func signalM(mp *m, sig int) { 534 tgkill(getpid(), int(mp.procid), sig) 535 } 536 537 // go118UseTimerCreateProfiler enables the per-thread CPU profiler. 538 const go118UseTimerCreateProfiler = true 539 540 // validSIGPROF compares this signal delivery's code against the signal sources 541 // that the profiler uses, returning whether the delivery should be processed. 542 // To be processed, a signal delivery from a known profiling mechanism should 543 // correspond to the best profiling mechanism available to this thread. Signals 544 // from other sources are always considered valid. 545 // 546 //go:nosplit 547 func validSIGPROF(mp *m, c *sigctxt) bool { 548 code := int32(c.sigcode()) 549 setitimer := code == _SI_KERNEL 550 timer_create := code == _SI_TIMER 551 552 if !(setitimer || timer_create) { 553 // The signal doesn't correspond to a profiling mechanism that the 554 // runtime enables itself. There's no reason to process it, but there's 555 // no reason to ignore it either. 556 return true 557 } 558 559 if mp == nil { 560 // Since we don't have an M, we can't check if there's an active 561 // per-thread timer for this thread. We don't know how long this thread 562 // has been around, and if it happened to interact with the Go scheduler 563 // at a time when profiling was active (causing it to have a per-thread 564 // timer). But it may have never interacted with the Go scheduler, or 565 // never while profiling was active. To avoid double-counting, process 566 // only signals from setitimer. 567 // 568 // When a custom cgo traceback function has been registered (on 569 // platforms that support runtime.SetCgoTraceback), SIGPROF signals 570 // delivered to a thread that cannot find a matching M do this check in 571 // the assembly implementations of runtime.cgoSigtramp. 572 return setitimer 573 } 574 575 // Having an M means the thread interacts with the Go scheduler, and we can 576 // check whether there's an active per-thread timer for this thread. 577 if atomic.Load(&mp.profileTimerValid) != 0 { 578 // If this M has its own per-thread CPU profiling interval timer, we 579 // should track the SIGPROF signals that come from that timer (for 580 // accurate reporting of its CPU usage; see issue 35057) and ignore any 581 // that it gets from the process-wide setitimer (to not over-count its 582 // CPU consumption). 583 return timer_create 584 } 585 586 // No active per-thread timer means the only valid profiler is setitimer. 587 return setitimer 588 } 589 590 func setProcessCPUProfiler(hz int32) { 591 setProcessCPUProfilerTimer(hz) 592 } 593 594 func setThreadCPUProfiler(hz int32) { 595 mp := getg().m 596 mp.profilehz = hz 597 598 if !go118UseTimerCreateProfiler { 599 return 600 } 601 602 // destroy any active timer 603 if atomic.Load(&mp.profileTimerValid) != 0 { 604 timerid := mp.profileTimer 605 atomic.Store(&mp.profileTimerValid, 0) 606 mp.profileTimer = 0 607 608 ret := timer_delete(timerid) 609 if ret != 0 { 610 print("runtime: failed to disable profiling timer; timer_delete(", timerid, ") errno=", -ret, "\n") 611 throw("timer_delete") 612 } 613 } 614 615 if hz == 0 { 616 // If the goal was to disable profiling for this thread, then the job's done. 617 return 618 } 619 620 // The period of the timer should be 1/Hz. For every "1/Hz" of additional 621 // work, the user should expect one additional sample in the profile. 622 // 623 // But to scale down to very small amounts of application work, to observe 624 // even CPU usage of "one tenth" of the requested period, set the initial 625 // timing delay in a different way: So that "one tenth" of a period of CPU 626 // spend shows up as a 10% chance of one sample (for an expected value of 627 // 0.1 samples), and so that "two and six tenths" periods of CPU spend show 628 // up as a 60% chance of 3 samples and a 40% chance of 2 samples (for an 629 // expected value of 2.6). Set the initial delay to a value in the unifom 630 // random distribution between 0 and the desired period. And because "0" 631 // means "disable timer", add 1 so the half-open interval [0,period) turns 632 // into (0,period]. 633 // 634 // Otherwise, this would show up as a bias away from short-lived threads and 635 // from threads that are only occasionally active: for example, when the 636 // garbage collector runs on a mostly-idle system, the additional threads it 637 // activates may do a couple milliseconds of GC-related work and nothing 638 // else in the few seconds that the profiler observes. 639 spec := new(itimerspec) 640 spec.it_value.setNsec(1 + int64(fastrandn(uint32(1e9/hz)))) 641 spec.it_interval.setNsec(1e9 / int64(hz)) 642 643 var timerid int32 644 var sevp sigevent 645 sevp.notify = _SIGEV_THREAD_ID 646 sevp.signo = _SIGPROF 647 sevp.sigev_notify_thread_id = int32(mp.procid) 648 ret := timer_create(_CLOCK_THREAD_CPUTIME_ID, &sevp, &timerid) 649 if ret != 0 { 650 // If we cannot create a timer for this M, leave profileTimerValid false 651 // to fall back to the process-wide setitimer profiler. 652 return 653 } 654 655 ret = timer_settime(timerid, 0, spec, nil) 656 if ret != 0 { 657 print("runtime: failed to configure profiling timer; timer_settime(", timerid, 658 ", 0, {interval: {", 659 spec.it_interval.tv_sec, "s + ", spec.it_interval.tv_nsec, "ns} value: {", 660 spec.it_value.tv_sec, "s + ", spec.it_value.tv_nsec, "ns}}, nil) errno=", -ret, "\n") 661 throw("timer_settime") 662 } 663 664 mp.profileTimer = timerid 665 atomic.Store(&mp.profileTimerValid, 1) 666 }