mirror of git://gcc.gnu.org/git/gcc.git
runtime: In backtraces, get inline functions, skip split-stack fns.
From-SVN: r195591
This commit is contained in:
parent
e60e09a0e0
commit
27741f93ef
|
|
@ -166,16 +166,16 @@ struct caller_ret
|
||||||
Caller (int skip)
|
Caller (int skip)
|
||||||
{
|
{
|
||||||
struct caller_ret ret;
|
struct caller_ret ret;
|
||||||
uintptr pc;
|
Location loc;
|
||||||
int32 n;
|
int32 n;
|
||||||
String fn;
|
|
||||||
|
|
||||||
runtime_memclr (&ret, sizeof ret);
|
runtime_memclr (&ret, sizeof ret);
|
||||||
n = runtime_callers (skip + 1, &pc, 1);
|
n = runtime_callers (skip + 1, &loc, 1);
|
||||||
if (n < 1)
|
if (n < 1)
|
||||||
return ret;
|
return ret;
|
||||||
ret.pc = pc;
|
ret.pc = loc.pc;
|
||||||
__go_file_line (pc, &fn, &ret.file, &ret.line);
|
ret.file = loc.filename;
|
||||||
|
ret.line = loc.lineno;
|
||||||
ret.ok = 1;
|
ret.ok = 1;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,20 +15,37 @@
|
||||||
|
|
||||||
struct callers_data
|
struct callers_data
|
||||||
{
|
{
|
||||||
uintptr *pcbuf;
|
Location *locbuf;
|
||||||
int index;
|
int index;
|
||||||
int max;
|
int max;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Callback function for backtrace_simple. Just collect the PC
|
/* Callback function for backtrace_full. Just collect the locations.
|
||||||
values. Return zero to continue, non-zero to stop. */
|
Return zero to continue, non-zero to stop. */
|
||||||
|
|
||||||
static int
|
static int
|
||||||
callback (void *data, uintptr_t pc)
|
callback (void *data, uintptr_t pc, const char *filename, int lineno,
|
||||||
|
const char *function)
|
||||||
{
|
{
|
||||||
struct callers_data *arg = (struct callers_data *) data;
|
struct callers_data *arg = (struct callers_data *) data;
|
||||||
|
Location *loc;
|
||||||
|
|
||||||
arg->pcbuf[arg->index] = pc;
|
/* Skip split stack functions. */
|
||||||
|
if (function != NULL)
|
||||||
|
{
|
||||||
|
const char *p = function;
|
||||||
|
|
||||||
|
if (__builtin_strncmp (p, "___", 3) == 0)
|
||||||
|
++p;
|
||||||
|
if (__builtin_strncmp (p, "__morestack_", 12) == 0)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
loc = &arg->locbuf[arg->index];
|
||||||
|
loc->pc = pc;
|
||||||
|
loc->filename = runtime_gostring ((const byte *) filename);
|
||||||
|
loc->function = runtime_gostring ((const byte *) function);
|
||||||
|
loc->lineno = lineno;
|
||||||
++arg->index;
|
++arg->index;
|
||||||
return arg->index >= arg->max;
|
return arg->index >= arg->max;
|
||||||
}
|
}
|
||||||
|
|
@ -47,15 +64,15 @@ error_callback (void *data __attribute__ ((unused)),
|
||||||
/* Gather caller PC's. */
|
/* Gather caller PC's. */
|
||||||
|
|
||||||
int32
|
int32
|
||||||
runtime_callers (int32 skip, uintptr *pcbuf, int32 m)
|
runtime_callers (int32 skip, Location *locbuf, int32 m)
|
||||||
{
|
{
|
||||||
struct callers_data data;
|
struct callers_data data;
|
||||||
|
|
||||||
data.pcbuf = pcbuf;
|
data.locbuf = locbuf;
|
||||||
data.index = 0;
|
data.index = 0;
|
||||||
data.max = m;
|
data.max = m;
|
||||||
backtrace_simple (__go_get_backtrace_state (), skip + 1, callback,
|
backtrace_full (__go_get_backtrace_state (), skip + 1, callback,
|
||||||
error_callback, &data);
|
error_callback, &data);
|
||||||
return data.index;
|
return data.index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -65,8 +82,19 @@ int Callers (int, struct __go_open_array)
|
||||||
int
|
int
|
||||||
Callers (int skip, struct __go_open_array pc)
|
Callers (int skip, struct __go_open_array pc)
|
||||||
{
|
{
|
||||||
|
Location *locbuf;
|
||||||
|
int ret;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
locbuf = (Location *) runtime_mal (pc.__count * sizeof (Location));
|
||||||
|
|
||||||
/* In the Go 1 release runtime.Callers has an off-by-one error,
|
/* In the Go 1 release runtime.Callers has an off-by-one error,
|
||||||
which we can not correct because it would break backward
|
which we can not correct because it would break backward
|
||||||
compatibility. Adjust SKIP here to be compatible. */
|
compatibility. Adjust SKIP here to be compatible. */
|
||||||
return runtime_callers (skip - 1, (uintptr *) pc.__values, pc.__count);
|
ret = runtime_callers (skip - 1, locbuf, pc.__count);
|
||||||
|
|
||||||
|
for (i = 0; i < ret; i++)
|
||||||
|
((uintptr *) pc.__values)[i] = locbuf[i].pc;
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,29 +13,25 @@
|
||||||
void
|
void
|
||||||
runtime_traceback ()
|
runtime_traceback ()
|
||||||
{
|
{
|
||||||
uintptr pcbuf[100];
|
Location locbuf[100];
|
||||||
int32 c;
|
int32 c;
|
||||||
|
|
||||||
c = runtime_callers (1, pcbuf, sizeof pcbuf / sizeof pcbuf[0]);
|
c = runtime_callers (1, locbuf, nelem (locbuf));
|
||||||
runtime_printtrace (pcbuf, c, true);
|
runtime_printtrace (locbuf, c, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
runtime_printtrace (uintptr *pcbuf, int32 c, bool current)
|
runtime_printtrace (Location *locbuf, int32 c, bool current)
|
||||||
{
|
{
|
||||||
int32 i;
|
int32 i;
|
||||||
|
|
||||||
for (i = 0; i < c; ++i)
|
for (i = 0; i < c; ++i)
|
||||||
{
|
{
|
||||||
String fn;
|
if (runtime_showframe (locbuf[i].function, current))
|
||||||
String file;
|
|
||||||
intgo line;
|
|
||||||
|
|
||||||
if (__go_file_line (pcbuf[i], &fn, &file, &line)
|
|
||||||
&& runtime_showframe (fn, current))
|
|
||||||
{
|
{
|
||||||
runtime_printf ("%S\n", fn);
|
runtime_printf ("%S\n", locbuf[i].function);
|
||||||
runtime_printf ("\t%S:%D\n", file, (int64) line);
|
runtime_printf ("\t%S:%D\n", locbuf[i].filename,
|
||||||
|
(int64) locbuf[i].lineno);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ package runtime
|
||||||
#include "malloc.h"
|
#include "malloc.h"
|
||||||
#include "defs.h"
|
#include "defs.h"
|
||||||
#include "go-type.h"
|
#include "go-type.h"
|
||||||
|
#include "go-string.h"
|
||||||
|
|
||||||
// NOTE(rsc): Everything here could use cas if contention became an issue.
|
// NOTE(rsc): Everything here could use cas if contention became an issue.
|
||||||
static Lock proflock;
|
static Lock proflock;
|
||||||
|
|
@ -46,7 +47,7 @@ struct Bucket
|
||||||
};
|
};
|
||||||
uintptr hash;
|
uintptr hash;
|
||||||
uintptr nstk;
|
uintptr nstk;
|
||||||
uintptr stk[1];
|
Location stk[1];
|
||||||
};
|
};
|
||||||
enum {
|
enum {
|
||||||
BuckHashSize = 179999,
|
BuckHashSize = 179999,
|
||||||
|
|
@ -58,9 +59,9 @@ static uintptr bucketmem;
|
||||||
|
|
||||||
// Return the bucket for stk[0:nstk], allocating new bucket if needed.
|
// Return the bucket for stk[0:nstk], allocating new bucket if needed.
|
||||||
static Bucket*
|
static Bucket*
|
||||||
stkbucket(int32 typ, uintptr *stk, int32 nstk, bool alloc)
|
stkbucket(int32 typ, Location *stk, int32 nstk, bool alloc)
|
||||||
{
|
{
|
||||||
int32 i;
|
int32 i, j;
|
||||||
uintptr h;
|
uintptr h;
|
||||||
Bucket *b;
|
Bucket *b;
|
||||||
|
|
||||||
|
|
@ -72,7 +73,7 @@ stkbucket(int32 typ, uintptr *stk, int32 nstk, bool alloc)
|
||||||
// Hash stack.
|
// Hash stack.
|
||||||
h = 0;
|
h = 0;
|
||||||
for(i=0; i<nstk; i++) {
|
for(i=0; i<nstk; i++) {
|
||||||
h += stk[i];
|
h += stk[i].pc;
|
||||||
h += h<<10;
|
h += h<<10;
|
||||||
h ^= h>>6;
|
h ^= h>>6;
|
||||||
}
|
}
|
||||||
|
|
@ -80,10 +81,18 @@ stkbucket(int32 typ, uintptr *stk, int32 nstk, bool alloc)
|
||||||
h ^= h>>11;
|
h ^= h>>11;
|
||||||
|
|
||||||
i = h%BuckHashSize;
|
i = h%BuckHashSize;
|
||||||
for(b = buckhash[i]; b; b=b->next)
|
for(b = buckhash[i]; b; b=b->next) {
|
||||||
if(b->typ == typ && b->hash == h && b->nstk == (uintptr)nstk &&
|
if(b->typ == typ && b->hash == h && b->nstk == (uintptr)nstk) {
|
||||||
runtime_mcmp((byte*)b->stk, (byte*)stk, nstk*sizeof stk[0]) == 0)
|
for(j = 0; j < nstk; j++) {
|
||||||
return b;
|
if(b->stk[j].pc != stk[j].pc ||
|
||||||
|
b->stk[j].lineno != stk[j].lineno ||
|
||||||
|
!__go_strings_equal(b->stk[j].filename, stk[j].filename))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (j == nstk)
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(!alloc)
|
if(!alloc)
|
||||||
return nil;
|
return nil;
|
||||||
|
|
@ -241,7 +250,7 @@ runtime_MProf_Malloc(void *p, uintptr size)
|
||||||
{
|
{
|
||||||
M *m;
|
M *m;
|
||||||
int32 nstk;
|
int32 nstk;
|
||||||
uintptr stk[32];
|
Location stk[32];
|
||||||
Bucket *b;
|
Bucket *b;
|
||||||
|
|
||||||
m = runtime_m();
|
m = runtime_m();
|
||||||
|
|
@ -298,7 +307,7 @@ runtime_blockevent(int64 cycles, int32 skip)
|
||||||
{
|
{
|
||||||
int32 nstk;
|
int32 nstk;
|
||||||
int64 rate;
|
int64 rate;
|
||||||
uintptr stk[32];
|
Location stk[32];
|
||||||
Bucket *b;
|
Bucket *b;
|
||||||
|
|
||||||
if(cycles <= 0)
|
if(cycles <= 0)
|
||||||
|
|
@ -336,7 +345,7 @@ record(Record *r, Bucket *b)
|
||||||
r->alloc_objects = b->allocs;
|
r->alloc_objects = b->allocs;
|
||||||
r->free_objects = b->frees;
|
r->free_objects = b->frees;
|
||||||
for(i=0; i<b->nstk && i<nelem(r->stk); i++)
|
for(i=0; i<b->nstk && i<nelem(r->stk); i++)
|
||||||
r->stk[i] = b->stk[i];
|
r->stk[i] = b->stk[i].pc;
|
||||||
for(; i<nelem(r->stk); i++)
|
for(; i<nelem(r->stk); i++)
|
||||||
r->stk[i] = 0;
|
r->stk[i] = 0;
|
||||||
}
|
}
|
||||||
|
|
@ -396,7 +405,7 @@ func BlockProfile(p Slice) (n int, ok bool) {
|
||||||
r->count = b->count;
|
r->count = b->count;
|
||||||
r->cycles = b->cycles;
|
r->cycles = b->cycles;
|
||||||
for(i=0; (uintptr)i<b->nstk && (uintptr)i<nelem(r->stk); i++)
|
for(i=0; (uintptr)i<b->nstk && (uintptr)i<nelem(r->stk); i++)
|
||||||
r->stk[i] = b->stk[i];
|
r->stk[i] = b->stk[i].pc;
|
||||||
for(; (uintptr)i<nelem(r->stk); i++)
|
for(; (uintptr)i<nelem(r->stk); i++)
|
||||||
r->stk[i] = 0;
|
r->stk[i] = 0;
|
||||||
}
|
}
|
||||||
|
|
@ -413,6 +422,7 @@ struct TRecord {
|
||||||
func ThreadCreateProfile(p Slice) (n int, ok bool) {
|
func ThreadCreateProfile(p Slice) (n int, ok bool) {
|
||||||
TRecord *r;
|
TRecord *r;
|
||||||
M *first, *mp;
|
M *first, *mp;
|
||||||
|
int32 i;
|
||||||
|
|
||||||
first = runtime_atomicloadp(&runtime_allm);
|
first = runtime_atomicloadp(&runtime_allm);
|
||||||
n = 0;
|
n = 0;
|
||||||
|
|
@ -423,7 +433,9 @@ func ThreadCreateProfile(p Slice) (n int, ok bool) {
|
||||||
ok = true;
|
ok = true;
|
||||||
r = (TRecord*)p.__values;
|
r = (TRecord*)p.__values;
|
||||||
for(mp=first; mp; mp=mp->alllink) {
|
for(mp=first; mp; mp=mp->alllink) {
|
||||||
runtime_memmove(r->stk, mp->createstack, sizeof r->stk);
|
for(i = 0; (uintptr)i < nelem(r->stk); i++) {
|
||||||
|
r->stk[i] = mp->createstack[i].pc;
|
||||||
|
}
|
||||||
r++;
|
r++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -473,10 +485,14 @@ func Stack(b Slice, all bool) (n int) {
|
||||||
static void
|
static void
|
||||||
saveg(G *gp, TRecord *r)
|
saveg(G *gp, TRecord *r)
|
||||||
{
|
{
|
||||||
int32 n;
|
int32 n, i;
|
||||||
|
Location locstk[nelem(r->stk)];
|
||||||
|
|
||||||
if(gp == runtime_g())
|
if(gp == runtime_g()) {
|
||||||
n = runtime_callers(0, r->stk, nelem(r->stk));
|
n = runtime_callers(0, locstk, nelem(r->stk));
|
||||||
|
for(i = 0; i < n; i++)
|
||||||
|
r->stk[i] = locstk[i].pc;
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
// FIXME: Not implemented.
|
// FIXME: Not implemented.
|
||||||
n = 0;
|
n = 0;
|
||||||
|
|
|
||||||
|
|
@ -631,7 +631,7 @@ runtime_goroutinetrailer(G *g)
|
||||||
struct Traceback
|
struct Traceback
|
||||||
{
|
{
|
||||||
G* gp;
|
G* gp;
|
||||||
uintptr pcbuf[100];
|
Location locbuf[100];
|
||||||
int32 c;
|
int32 c;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -677,7 +677,7 @@ runtime_tracebackothers(G * volatile me)
|
||||||
runtime_gogo(gp);
|
runtime_gogo(gp);
|
||||||
}
|
}
|
||||||
|
|
||||||
runtime_printtrace(tb.pcbuf, tb.c, false);
|
runtime_printtrace(tb.locbuf, tb.c, false);
|
||||||
runtime_goroutinetrailer(gp);
|
runtime_goroutinetrailer(gp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -692,8 +692,8 @@ gtraceback(G* gp)
|
||||||
|
|
||||||
traceback = gp->traceback;
|
traceback = gp->traceback;
|
||||||
gp->traceback = nil;
|
gp->traceback = nil;
|
||||||
traceback->c = runtime_callers(1, traceback->pcbuf,
|
traceback->c = runtime_callers(1, traceback->locbuf,
|
||||||
sizeof traceback->pcbuf / sizeof traceback->pcbuf[0]);
|
sizeof traceback->locbuf / sizeof traceback->locbuf[0]);
|
||||||
runtime_gogo(traceback->gp);
|
runtime_gogo(traceback->gp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1742,13 +1742,14 @@ static struct {
|
||||||
void (*fn)(uintptr*, int32);
|
void (*fn)(uintptr*, int32);
|
||||||
int32 hz;
|
int32 hz;
|
||||||
uintptr pcbuf[100];
|
uintptr pcbuf[100];
|
||||||
|
Location locbuf[100];
|
||||||
} prof;
|
} prof;
|
||||||
|
|
||||||
// Called if we receive a SIGPROF signal.
|
// Called if we receive a SIGPROF signal.
|
||||||
void
|
void
|
||||||
runtime_sigprof()
|
runtime_sigprof()
|
||||||
{
|
{
|
||||||
int32 n;
|
int32 n, i;
|
||||||
|
|
||||||
if(prof.fn == nil || prof.hz == 0)
|
if(prof.fn == nil || prof.hz == 0)
|
||||||
return;
|
return;
|
||||||
|
|
@ -1758,7 +1759,9 @@ runtime_sigprof()
|
||||||
runtime_unlock(&prof);
|
runtime_unlock(&prof);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
n = runtime_callers(0, prof.pcbuf, nelem(prof.pcbuf));
|
n = runtime_callers(0, prof.locbuf, nelem(prof.locbuf));
|
||||||
|
for(i = 0; i < n; i++)
|
||||||
|
prof.pcbuf[i] = prof.locbuf[i].pc;
|
||||||
if(n > 0)
|
if(n > 0)
|
||||||
prof.fn(prof.pcbuf, n);
|
prof.fn(prof.pcbuf, n);
|
||||||
runtime_unlock(&prof);
|
runtime_unlock(&prof);
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,8 @@ typedef struct __go_map_type MapType;
|
||||||
|
|
||||||
typedef struct Traceback Traceback;
|
typedef struct Traceback Traceback;
|
||||||
|
|
||||||
|
typedef struct Location Location;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Per-CPU declaration.
|
* Per-CPU declaration.
|
||||||
*/
|
*/
|
||||||
|
|
@ -155,6 +157,16 @@ struct GCStats
|
||||||
uint64 nosyield;
|
uint64 nosyield;
|
||||||
uint64 nsleep;
|
uint64 nsleep;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// A location in the program, used for backtraces.
|
||||||
|
struct Location
|
||||||
|
{
|
||||||
|
uintptr pc;
|
||||||
|
String filename;
|
||||||
|
String function;
|
||||||
|
intgo lineno;
|
||||||
|
};
|
||||||
|
|
||||||
struct G
|
struct G
|
||||||
{
|
{
|
||||||
Defer* defer;
|
Defer* defer;
|
||||||
|
|
@ -226,7 +238,7 @@ struct M
|
||||||
MCache *mcache;
|
MCache *mcache;
|
||||||
G* lockedg;
|
G* lockedg;
|
||||||
G* idleg;
|
G* idleg;
|
||||||
uintptr createstack[32]; // Stack that created this thread.
|
Location createstack[32]; // Stack that created this thread.
|
||||||
M* nextwaitm; // next M waiting for lock
|
M* nextwaitm; // next M waiting for lock
|
||||||
uintptr waitsema; // semaphore for parking on locks
|
uintptr waitsema; // semaphore for parking on locks
|
||||||
uint32 waitsemacount;
|
uint32 waitsemacount;
|
||||||
|
|
@ -391,7 +403,8 @@ void runtime_goroutineheader(G*);
|
||||||
void runtime_goroutinetrailer(G*);
|
void runtime_goroutinetrailer(G*);
|
||||||
void runtime_traceback();
|
void runtime_traceback();
|
||||||
void runtime_tracebackothers(G*);
|
void runtime_tracebackothers(G*);
|
||||||
void runtime_printtrace(uintptr*, int32, bool);
|
void runtime_printtrace(Location*, int32, bool);
|
||||||
|
String runtime_gostring(const byte*);
|
||||||
String runtime_gostringnocopy(const byte*);
|
String runtime_gostringnocopy(const byte*);
|
||||||
void* runtime_mstart(void*);
|
void* runtime_mstart(void*);
|
||||||
G* runtime_malg(int32, byte**, size_t*);
|
G* runtime_malg(int32, byte**, size_t*);
|
||||||
|
|
@ -406,7 +419,7 @@ void runtime_entersyscall(void) __asm__ (GOSYM_PREFIX "syscall.Entersyscall");
|
||||||
void runtime_exitsyscall(void) __asm__ (GOSYM_PREFIX "syscall.Exitsyscall");
|
void runtime_exitsyscall(void) __asm__ (GOSYM_PREFIX "syscall.Exitsyscall");
|
||||||
void siginit(void);
|
void siginit(void);
|
||||||
bool __go_sigsend(int32 sig);
|
bool __go_sigsend(int32 sig);
|
||||||
int32 runtime_callers(int32, uintptr*, int32);
|
int32 runtime_callers(int32, Location*, int32);
|
||||||
int64 runtime_nanotime(void);
|
int64 runtime_nanotime(void);
|
||||||
int64 runtime_cputicks(void);
|
int64 runtime_cputicks(void);
|
||||||
int64 runtime_tickspersecond(void);
|
int64 runtime_tickspersecond(void);
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,8 @@ package runtime
|
||||||
|
|
||||||
#define charntorune(pv, str, len) __go_get_rune(str, len, pv)
|
#define charntorune(pv, str, len) __go_get_rune(str, len, pv)
|
||||||
|
|
||||||
|
const String runtime_emptystring;
|
||||||
|
|
||||||
intgo
|
intgo
|
||||||
runtime_findnull(const byte *s)
|
runtime_findnull(const byte *s)
|
||||||
{
|
{
|
||||||
|
|
@ -18,6 +20,38 @@ runtime_findnull(const byte *s)
|
||||||
return __builtin_strlen((const char*) s);
|
return __builtin_strlen((const char*) s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static String
|
||||||
|
gostringsize(intgo l, byte** pmem)
|
||||||
|
{
|
||||||
|
String s;
|
||||||
|
byte *mem;
|
||||||
|
|
||||||
|
if(l == 0) {
|
||||||
|
*pmem = nil;
|
||||||
|
return runtime_emptystring;
|
||||||
|
}
|
||||||
|
// leave room for NUL for C runtime (e.g., callers of getenv)
|
||||||
|
mem = runtime_mallocgc(l+1, FlagNoPointers, 1, 0);
|
||||||
|
s.str = mem;
|
||||||
|
s.len = l;
|
||||||
|
mem[l] = 0;
|
||||||
|
*pmem = mem;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
String
|
||||||
|
runtime_gostring(const byte *str)
|
||||||
|
{
|
||||||
|
intgo l;
|
||||||
|
String s;
|
||||||
|
byte *mem;
|
||||||
|
|
||||||
|
l = runtime_findnull(str);
|
||||||
|
s = gostringsize(l, &mem);
|
||||||
|
runtime_memmove(mem, str, l);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
String
|
String
|
||||||
runtime_gostringnocopy(const byte *str)
|
runtime_gostringnocopy(const byte *str)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue