From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail1-relais-roc.national.inria.fr (mail1-relais-roc.national.inria.fr [192.134.164.82]) by walapai.inria.fr (8.13.6/8.13.6) with ESMTP id q37DRo3o005161 for ; Sat, 7 Apr 2012 15:27:50 +0200 X-IronPort-Anti-Spam-Filtered: true X-IronPort-Anti-Spam-Result: AqMCAIo/gE/RVdK2mGdsb2JhbABFsBcBiH0IIgEBAQEBCAkNBxQnggkBAQEDARICExkBGx0BAwELBgULAwouIQEBEQEFARwGEwkSB4ddAQMGBQubPwqMGYJxhFoKGScNV4h2AQULiiKGLQSVbIERiiiDHT2EDA X-IronPort-AV: E=Sophos;i="4.75,385,1330902000"; d="scan'208";a="153120065" Received: from mail-iy0-f182.google.com ([209.85.210.182]) by mail1-smtp-roc.national.inria.fr with ESMTP/TLS/RC4-SHA; 07 Apr 2012 15:27:44 +0200 Received: by iahk25 with SMTP id k25so6673633iah.27 for ; Sat, 07 Apr 2012 06:27:43 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:in-reply-to:references:date:message-id:subject:from:to :cc:content-type; bh=bQMXrGas7sUV33mpldEKeIU1akB/V3wtRuX2KuGu2nc=; b=jIeJtUG8anBUffzU0fBq+8TFiyBcgk8J3YBXZXE20UFiY6y8wsJKNpYO+D+c/q3O1S xhlrffn7su6oBzIi32AHMQka4Wua2mMAc2X0rWp5nxuD7WFN4NwaN9mAW8aNu1uRHT30 SuxlHA+co+i9WASrH1+XhGjZTPBwczgEjortEtjUtOYVkUCwIcsU3O9ALN9woToPhzcm xZ6mf9UAZnPV5ALYTVpXK3fprHYarpuPHH2Ne5xoy/Wg/3Qn7ihh1JqCqq4woVxvYpdV 5cDCcQaU5F632zNZz7JOlTJDDT8Q0SoFhdA0XaJRr804Msn7vzk9lfQF3XF2ez1plHh8 baSw== MIME-Version: 1.0 Received: by 10.42.176.6 with SMTP id bc6mr769776icb.49.1333805263512; Sat, 07 Apr 2012 06:27:43 -0700 (PDT) Received: by 10.42.117.67 with HTTP; Sat, 7 Apr 2012 06:27:43 -0700 (PDT) In-Reply-To: <1333544144.2826.449.camel@thinkpad> References: <20120401195733.GB15870@annexia.org> <20120403121327.GA29159@pps.jussieu.fr> <1333544144.2826.449.camel@thinkpad> Date: Sat, 7 Apr 2012 15:27:43 +0200 Message-ID: From: Hans Ole Rafaelsen To: Gerd Stolpmann Cc: Gabriel Scherer , caml-list@inria.fr Content-Type: multipart/alternative; boundary=90e6ba6e9014ee65bd04bd16bcf2 Subject: Re: [Caml-list] Strategies for finding memory leaks --90e6ba6e9014ee65bd04bd16bcf2 Content-Type: text/plain; charset=ISO-8859-1 So just to be clear, it seems like I'm allocating lots of objects of a kind that I don't free. I have been trying to tracking down this in my ML part that use the library. You suggest that trying to have a counter in the C binding part of the library and count each time a object is created (and maybe each time it is destroyed) might be a better option. I have not worked much with ML<->C binding code, just want to be sure that this might be a proper way to do it before I start. -- Hans Ole On Wed, Apr 4, 2012 at 2:55 PM, Gerd Stolpmann wrote: > Am Mittwoch, den 04.04.2012, 13:30 +0200 schrieb Gabriel Scherer: > > May your program leak one of those GTK resources? > > > > The effectiveness of your patch seems to indicate that you have a lot > > of one of these values allocated (and that they were requesting the GC > > much too frequently). The patch solves the CPU usage induced by > > additional GC, but does not change the reason why those GC were > > launched: apparently your code allocates a lot of those resources. If > > there indeed is a leak in your program, it will use more and more > > memory even if you fix the CPU-usage effect. > > > > An interesting side-effect of your patch is that you could, by > > selectively disabling some of the change you made (eg. by changing > > Val_g_boxed but not Val_g_boxed_new), isolate which of those resources > > were provoking the increased CPU usage, because it was allocated in > > high number. > > Or just increment a counter for each type. > > Gerd > > > (Usual candidates that provoke leak are global data structures that > > store references to your data. A closure will also reference the data > > corresponding to the variables it captures, so storing closures in > > such tables can be an indirect cause for "leaks". Do you have global > > tables of callbacks or values for GTK-land?) > > > > On Wed, Apr 4, 2012 at 12:53 PM, Hans Ole Rafaelsen > > wrote: > > > Hi, > > > > > > Thanks for your suggestions. I tried to patch lablgtk2 with: > > > > > > --- src/ml_gdkpixbuf.c.orig 2012-04-03 13:56:29.618264702 +0200 > > > +++ src/ml_gdkpixbuf.c 2012-04-03 13:56:58.106263510 +0200 > > > @@ -119,7 +119,7 @@ > > > value ret; > > > if (pb == NULL) ml_raise_null_pointer(); > > > ret = alloc_custom (&ml_custom_GdkPixbuf, sizeof pb, > > > - 100, 1000); > > > + 0, 1); > > > p = Data_custom_val (ret); > > > *p = ref ? g_object_ref (pb) : pb; > > > return ret; > > > > > > --- src/ml_gobject.c.orig 2012-04-03 15:40:11.002004506 +0200 > > > +++ src/ml_gobject.c 2012-04-03 15:41:04.938002250 +0200 > > > @@ -219,7 +219,7 @@ > > > CAMLprim value ml_g_value_new(void) > > > { > > > value ret = alloc_custom(&ml_custom_GValue, > > > sizeof(value)+sizeof(GValue), > > > - 20, 1000); > > > + 0, 1); > > > /* create an MLPointer */ > > > Field(ret,1) = (value)2; > > > ((GValue*)&Field(ret,2))->g_type = 0; > > > @@ -272,14 +272,14 @@ > > > custom_serialize_default, custom_deserialize_default }; > > > CAMLprim value Val_gboxed(GType t, gpointer p) > > > { > > > - value ret = alloc_custom(&ml_custom_gboxed, 2*sizeof(value), 10, > 1000); > > > + value ret = alloc_custom(&ml_custom_gboxed, 2*sizeof(value), 0, > 1); > > > Store_pointer(ret, g_boxed_copy (t,p)); > > > Field(ret,2) = (value)t; > > > return ret; > > > } > > > CAMLprim value Val_gboxed_new(GType t, gpointer p) > > > { > > > - value ret = alloc_custom(&ml_custom_gboxed, 2*sizeof(value), 10, > 1000); > > > + value ret = alloc_custom(&ml_custom_gboxed, 2*sizeof(value), 0, > 1); > > > Store_pointer(ret, p); > > > Field(ret,2) = (value)t; > > > return ret; > > > > > > > > > > > > At startup is uses > > > top - 16:40:27 up 1 day, 7:01, 28 users, load average: 0.47, 0.50, > 0.35 > > > Tasks: 1 total, 0 running, 1 sleeping, 0 stopped, 0 zombie > > > Cpu(s): 4.8%us, 1.3%sy, 0.0%ni, 93.6%id, 0.2%wa, 0.0%hi, 0.1%si, > > > 0.0%st > > > Mem: 4004736k total, 3617960k used, 386776k free, 130704k > buffers > > > Swap: 4070396k total, 9244k used, 4061152k free, 1730344k cached > > > > > > PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ > > > COMMAND > > > 10275 hans 20 0 529m 77m 13m S 14 2.0 0:01.66 > > > vc_client.nativ > > > > > > and 12 hours later > > > top - 04:40:07 up 1 day, 19:01, 35 users, load average: 0.00, 0.01, > 0.05 > > > Tasks: 1 total, 0 running, 1 sleeping, 0 stopped, 0 zombie > > > Cpu(s): 20.2%us, 3.4%sy, 0.0%ni, 76.1%id, 0.1%wa, 0.0%hi, 0.2%si, > > > 0.0%st > > > Mem: 4004736k total, 3828308k used, 176428k free, 143928k > buffers > > > Swap: 4070396k total, 10708k used, 4059688k free, 1756524k cached > > > > > > PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ > > > COMMAND > > > 10275 hans 20 0 534m 82m 13m S 17 2.1 110:11.19 > > > vc_client.nativ > > > > > > Without the patch > > > top - 22:05:38 up 1 day, 12:26, 34 users, load average: 0.35, 0.16, > 0.13 > > > Tasks: 1 total, 0 running, 1 sleeping, 0 stopped, 0 zombie > > > Cpu(s): 5.6%us, 1.5%sy, 0.0%ni, 92.6%id, 0.2%wa, 0.0%hi, 0.1%si, > > > 0.0%st > > > Mem: 4004736k total, 3868136k used, 136600k free, 140900k > buffers > > > Swap: 4070396k total, 9680k used, 4060716k free, 1837500k cached > > > > > > PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ > > > COMMAND > > > 25111 hans 20 0 453m 76m 13m S 14 2.0 0:13.68 > vc_client_old.n > > > > > > top - 10:05:19 up 2 days, 26 min, 35 users, load average: 0.01, 0.04, > 0.05 > > > Tasks: 1 total, 0 running, 1 sleeping, 0 stopped, 0 zombie > > > Cpu(s): 20.4%us, 3.2%sy, 0.0%ni, 75.8%id, 0.4%wa, 0.0%hi, 0.2%si, > > > 0.0%st > > > Mem: 4004736k total, 3830596k used, 174140k free, 261692k > buffers > > > Swap: 4070396k total, 13640k used, 4056756k free, 1640452k cached > > > > > > PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ > > > COMMAND > > > 25111 hans 20 0 453m 76m 13m S 49 2.0 263:05.34 > > > vc_client_old.n > > > > > > So from this it seems that with the patch it still uses more and more > CPU, > > > but at a much lower rate. However, it seems to increase memory usage > with > > > the patch. I also tried to patch the wrappers.h file, but the memory > > > consumption just exploded. > > > > > > So it is working better, but still not good enough. Is there some way > to > > > prevent this kind of behavior? That is, no extra memory usage and no > extra > > > CPU usage. > > > > > > I have attached some additional profiling if that would be of any > interest. > > > In short it seems to be that it is the GC that is consuming the CPU. > > > > > > Best, > > > > > > Hans Ole > > > > > > > > > On Tue, Apr 3, 2012 at 2:13 PM, Jerome Vouillon < > vouillon@pps.jussieu.fr> > > > wrote: > > >> > > >> On Tue, Apr 03, 2012 at 12:42:08PM +0200, Gerd Stolpmann wrote: > > >> > This reminds me of a problem I had with a specific C binding (for > > >> > mysql), > > >> > years ago. That binding allocated custom blocks with badly chosen > > >> > parameters used/max (see the docs for caml_alloc_custom in > > >> > http://caml.inria.fr/pub/docs/manual-ocaml/manual032.html#toc144). > If > > >> > the > > >> > ratio used/max is > 0, these parameters accelerate the GC. If the > custom > > >> > blocks are frequently allocated, this can have a dramatic effect, > even > > >> > for > > >> > quite small used/max ratios. The solution was to change the code, > and to > > >> > set used=0 and max=1. > > >> > > > >> > This type of problem would match your observation that the GC works > more > > >> > and more the longer the program runs, i.e. the more custom blocks > have > > >> > been allocated. > > >> > > > >> > The problem basically also exists with bigarrays - with > > >> > used= and max=256M (hardcoded). > > >> > > >> I have also observed this with input-output channels (in_channel and > > >> out_channel), where used = 1 and max = 1000. A full major GC is > > >> performed every time a thousand files are opened, which can result on > > >> a significant overhead when you open lot of files and the heap is > > >> large. > > >> > > >> -- Jerome > > > > > > > > > > > > -- > ------------------------------------------------------------ > Gerd Stolpmann, Darmstadt, Germany gerd@gerd-stolpmann.de > Creator of GODI and camlcity.org. > Contact details: http://www.camlcity.org/contact.html > Company homepage: http://www.gerd-stolpmann.de > *** Searching for new projects! Need consulting for system > *** programming in Ocaml? Gerd Stolpmann can help you. > ------------------------------------------------------------ > > --90e6ba6e9014ee65bd04bd16bcf2 Content-Type: text/html; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable So just to be clear, it seems like I'm allocating lots of objects of a = kind that I don't free. I have been trying to tracking down this in my = ML part that use the library. You suggest that trying to have a counter in = the C binding part of the library and count each time a object is created (= and maybe each time it is destroyed) might be a better option. I have not w= orked much with ML<->C binding code, just want to be sure that this m= ight be a proper way to do it before I start.

--
Hans Ole

On Wed, Apr 4, 2012 a= t 2:55 PM, Gerd Stolpmann <info@gerd-stolpmann.de> wrote:
Am Mittwoch, den 04.04.2012, 13:30 +0200 schrieb Gabriel Scherer:
> May your program leak one of those GTK resources?
>
> The effectiveness of your patch seems to indicate that you have a lot<= br> > of one of these values allocated (and that they were requesting the GC=
> much too frequently). The patch solves the CPU usage induced by
> additional GC, but does not change the reason why those GC were
> launched: apparently your code allocates a lot of those resources. If<= br> > there indeed is a leak in your program, it will use more and more
> memory even if you fix the CPU-usage effect.
>
> An interesting side-effect of your patch is that you could, by
> selectively disabling some of the change you made (eg. by changing
> Val_g_boxed but not Val_g_boxed_new), isolate which of those resources=
> were provoking the increased CPU usage, because it was allocated in
> high number.

Or just increment a counter for each type.

Gerd

> (Usual candidates that provoke leak are global data structures that
> store references to your data. A closure will also reference the data<= br> > corresponding to the variables it captures, so storing closures in
> such tables can be an indirect cause for "leaks". Do you hav= e global
> tables of callbacks or values for GTK-land?)
>
> On Wed, Apr 4, 2012 at 12:53 PM, Hans Ole Rafaelsen
> <hrafaelsen@gmail.com&g= t; wrote:
> > Hi,
> >
> > Thanks for your suggestions. I tried to patch lablgtk2 with:
> >
> > --- src/ml_gdkpixbuf.c.orig =A0 =A0 2012-04-03 13:56:29.618264702= +0200
> > +++ src/ml_gdkpixbuf.c =A02012-04-03 13:56:58.106263510 +0200
> > @@ -119,7 +119,7 @@
> > =A0 =A0value ret;
> > =A0 =A0if (pb =3D=3D NULL) ml_raise_null_pointer();
> > =A0 =A0ret =3D alloc_custom (&ml_custom_GdkPixbuf, sizeof pb,=
> > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 100, 1000);
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 0, 1);
> > =A0 =A0p =3D Data_custom_val (ret);
> > =A0 =A0*p =3D ref ? g_object_ref (pb) : pb;
> > =A0 =A0return ret;
> >
> > --- src/ml_gobject.c.orig =A0 =A0 =A0 2012-04-03 15:40:11.0020045= 06 +0200
> > +++ src/ml_gobject.c =A0 =A02012-04-03 15:41:04.938002250 +0200 > > @@ -219,7 +219,7 @@
> > =A0CAMLprim value ml_g_value_new(void)
> > =A0{
> > =A0 =A0 =A0value ret =3D alloc_custom(&ml_custom_GValue,
> > sizeof(value)+sizeof(GValue),
> > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 20, 100= 0);
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 0, 1);<= br> > > =A0 =A0 =A0/* create an MLPointer */
> > =A0 =A0 =A0Field(ret,1) =3D (value)2;
> > =A0 =A0 =A0((GValue*)&Field(ret,2))->g_type =3D 0;
> > @@ -272,14 +272,14 @@
> > =A0 =A0custom_serialize_default, custom_deserialize_default };
> > =A0CAMLprim value Val_gboxed(GType t, gpointer p)
> > =A0{
> > - =A0 =A0value ret =3D alloc_custom(&ml_custom_gboxed, 2*size= of(value), 10, 1000);
> > + =A0 =A0value ret =3D alloc_custom(&ml_custom_gboxed, 2*size= of(value), 0, 1);
> > =A0 =A0 =A0Store_pointer(ret, g_boxed_copy (t,p));
> > =A0 =A0 =A0Field(ret,2) =3D (value)t;
> > =A0 =A0 =A0return ret;
> > =A0}
> > =A0CAMLprim value Val_gboxed_new(GType t, gpointer p)
> > =A0{
> > - =A0 =A0value ret =3D alloc_custom(&ml_custom_gboxed, 2*size= of(value), 10, 1000);
> > + =A0 =A0value ret =3D alloc_custom(&ml_custom_gboxed, 2*size= of(value), 0, 1);
> > =A0 =A0 =A0Store_pointer(ret, p);
> > =A0 =A0 =A0Field(ret,2) =3D (value)t;
> > =A0 =A0 =A0return ret;
> >
> >
> >
> > At startup is uses
> > top - 16:40:27 up 1 day, =A07:01, 28 users, =A0load average: 0.47= , 0.50, 0.35
> > Tasks: =A0 1 total, =A0 0 running, =A0 1 sleeping, =A0 0 stopped,= =A0 0 zombie
> > Cpu(s): =A04.8%us, =A01.3%sy, =A00.0%ni, 93.6%id, =A00.2%wa, =A00= .0%hi, =A00.1%si,
> > 0.0%st
> > Mem: =A0 4004736k total, =A03617960k used, =A0 386776k free, =A0 = 130704k buffers
> > Swap: =A04070396k total, =A0 =A0 9244k used, =A04061152k free, = =A01730344k cached
> >
> > =A0 PID USER =A0 =A0 =A0PR =A0NI =A0VIRT =A0RES =A0SHR S %CPU %ME= M =A0 =A0TIME+
> > COMMAND
> > 10275 hans =A0 =A0 =A020 =A0 0 =A0529m =A077m =A013m S =A0 14 =A0= 2.0 =A0 0:01.66
> > vc_client.nativ
> >
> > and 12 hours later
> > top - 04:40:07 up 1 day, 19:01, 35 users, =A0load average: 0.00, = 0.01, 0.05
> > Tasks: =A0 1 total, =A0 0 running, =A0 1 sleeping, =A0 0 stopped,= =A0 0 zombie
> > Cpu(s): 20.2%us, =A03.4%sy, =A00.0%ni, 76.1%id, =A00.1%wa, =A00.0= %hi, =A00.2%si,
> > 0.0%st
> > Mem: =A0 4004736k total, =A03828308k used, =A0 176428k free, =A0 = 143928k buffers
> > Swap: =A04070396k total, =A0 =A010708k used, =A04059688k free, = =A01756524k cached
> >
> > =A0 PID USER =A0 =A0 =A0PR =A0NI =A0VIRT =A0RES =A0SHR S %CPU %ME= M =A0 =A0TIME+
> > COMMAND
> > 10275 hans =A0 =A0 =A020 =A0 0 =A0534m =A082m =A013m S =A0 17 =A0= 2.1 110:11.19
> > vc_client.nativ
> >
> > Without the patch
> > top - 22:05:38 up 1 day, 12:26, 34 users, =A0load average: 0.35, = 0.16, 0.13
> > Tasks: =A0 1 total, =A0 0 running, =A0 1 sleeping, =A0 0 stopped,= =A0 0 zombie
> > Cpu(s): =A05.6%us, =A01.5%sy, =A00.0%ni, 92.6%id, =A00.2%wa, =A00= .0%hi, =A00.1%si,
> > 0.0%st
> > Mem: =A0 4004736k total, =A03868136k used, =A0 136600k free, =A0 = 140900k buffers
> > Swap: =A04070396k total, =A0 =A0 9680k used, =A04060716k free, = =A01837500k cached
> >
> > =A0 PID USER =A0 =A0 =A0PR =A0NI =A0VIRT =A0RES =A0SHR S %CPU %ME= M =A0 =A0TIME+
> > COMMAND
> > 25111 hans =A0 =A0 =A020 =A0 0 =A0453m =A076m =A013m S =A0 14 =A0= 2.0 =A0 0:13.68 vc_client_old.n
> >
> > top - 10:05:19 up 2 days, 26 min, 35 users, =A0load average: 0.01= , 0.04, 0.05
> > Tasks: =A0 1 total, =A0 0 running, =A0 1 sleeping, =A0 0 stopped,= =A0 0 zombie
> > Cpu(s): 20.4%us, =A03.2%sy, =A00.0%ni, 75.8%id, =A00.4%wa, =A00.0= %hi, =A00.2%si,
> > 0.0%st
> > Mem: =A0 4004736k total, =A03830596k used, =A0 174140k free, =A0 = 261692k buffers
> > Swap: =A04070396k total, =A0 =A013640k used, =A04056756k free, = =A01640452k cached
> >
> > =A0 PID USER =A0 =A0 =A0PR =A0NI =A0VIRT =A0RES =A0SHR S %CPU %ME= M =A0 =A0TIME+
> > COMMAND
> > 25111 hans =A0 =A0 =A020 =A0 0 =A0453m =A076m =A013m S =A0 49 =A0= 2.0 263:05.34
> > vc_client_old.n
> >
> > So from this it seems that with the patch it still uses more and = more CPU,
> > but at a much lower rate. However, it seems to increase memory us= age with
> > the patch. I also tried to patch the wrappers.h file, but the mem= ory
> > consumption just exploded.
> >
> > So it is working better, but still not good enough. Is there some= way to
> > prevent this kind of behavior? That is, no extra memory usage and= no extra
> > CPU usage.
> >
> > I have attached some additional profiling if that would be of any= interest.
> > In short it seems to be that it is the GC that is consuming the C= PU.
> >
> > Best,
> >
> > Hans Ole
> >
> >
> > On Tue, Apr 3, 2012 at 2:13 PM, Jerome Vouillon <vouillon@pps.jussieu.fr>
> > wrote:
> >>
> >> On Tue, Apr 03, 2012 at 12:42:08PM +0200, Gerd Stolpmann wrot= e:
> >> > This reminds me of a problem I had with a specific C bin= ding (for
> >> > mysql),
> >> > years ago. That binding allocated custom blocks with bad= ly chosen
> >> > parameters used/max (see the docs for caml_alloc_custom = in
> >> > http://caml.inria.fr/pub/docs/manual= -ocaml/manual032.html#toc144). If
> >> > the
> >> > ratio used/max is > 0, these parameters accelerate th= e GC. If the custom
> >> > blocks are frequently allocated, this can have a dramati= c effect, even
> >> > for
> >> > quite small used/max ratios. The solution was to change = the code, and to
> >> > set used=3D0 and max=3D1.
> >> >
> >> > This type of problem would match your observation that t= he GC works more
> >> > and more the longer the program runs, i.e. the more cust= om blocks have
> >> > been allocated.
> >> >
> >> > The problem basically also exists with bigarrays - with<= br> > >> > used=3D<size_of_bigarary> and max=3D256M (hardcode= d).
> >>
> >> I have also observed this with input-output channels (in_chan= nel and
> >> out_channel), where used =3D 1 and max =3D 1000. A full major= GC is
> >> performed every time a thousand files are opened, which can r= esult on
> >> a significant overhead when you open lot of files and the hea= p is
> >> large.
> >>
> >> -- Jerome
> >
> >
>
>

--
------------------------------------------------------------
Gerd Stolpmann, Darmstadt, Germany =A0 =A0gerd@gerd-stolpmann.de
Creator of GODI and camlc= ity.org.
Contact details: =A0 =A0 =A0 =A0http://www.camlcity.org/contact.html
Company homepage: =A0 =A0 =A0 http://www.gerd-stolpmann.de
*** Searching for new projects! Need consulting for system
*** programming in Ocaml? Gerd Stolpmann can help you.
------------------------------------------------------------


--90e6ba6e9014ee65bd04bd16bcf2--