Я пишу библиотеку Sqlite для Java, которая использует код C Sqlite3 поверх JNI.
Я думаю, что у меня проблема с утечками памяти. Может ли кто-нибудь помочь мне с просмотром моего кода и сказать, что еще мне нужно бесплатно?
вот моя библиотека:
#include <jni.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sqlite3.h"
#include "dynamicArray.h"
void throwSqliteException(JNIEnv * env, const char * message) {
jclass ex = (*env)->FindClass(env, "sqliteNative/SqliteException");
(*env)->ThrowNew(env, ex, message);
}
JNIEXPORT jstring JNICALL Java_sqliteNative_Sqlite_getCVersion(JNIEnv * env, jclass obj) {
return (*env)->NewStringUTF(env, "1.0.3");
}
JNIEXPORT jobjectArray JNICALL Java_sqliteNative_Sqlite_query(JNIEnv * env, jobject obj, jstring database, jstring sql) {
const char * jDatabase = (*env)->GetStringUTFChars(env, database, NULL);
if (jDatabase == NULL)
return NULL;
const char * jSql = (*env)->GetStringUTFChars(env, sql, NULL);
if (jSql == NULL)
return NULL;
sqlite3 * db;
sqlite3_stmt * stmt;
const char * leftOver = 0;
jobjectArray table = NULL;
//printf("opening\n");
if (sqlite3_open(jDatabase, &db)) {
throwSqliteException(env, sqlite3_errmsg(db));
} else {
//printf("open ok\n");
const char * sqlExec = jSql;
// run queries for multiple sqls
// until left over is empty
do {
//printf("preparing\n");
int rc = sqlite3_prepare_v2(db, sqlExec, -1, &stmt, &leftOver);
//printf("prepare ok\n");
if (rc) {
throwSqliteException(env, sqlite3_errmsg(db));
break;
}
// change executing sql to next left over
sqlExec = leftOver;
//printf("getting coll count\n");
int cols = sqlite3_column_count(stmt);
//printf("coll count ok\n");
//printf("initing dynamic array\n");
dynamicArray * da = dynamicArray_init();
//printf("init ok\n");
jobjectArray row;
while (1) {
//printf("stepping\n");
int retval = sqlite3_step(stmt);
//printf("step ok\n");
if (retval == SQLITE_ROW) {
// only return data if it is a last executing sql
if (strlen(leftOver)) {
break;
}
//printf("creating row object array with size %d\n", cols);
row = (*env)->NewObjectArray(env, cols, (*env)->FindClass(env, "java/lang/String"), 0);
//printf("create ok\n");
jobjectArray * rowCopy = calloc(1, sizeof (row));
*rowCopy = row;
// add row to dynamic array
//printf("adding to da\n");
dynamicArray_add(da, rowCopy);
//printf("add ok\n");
int col;
for (col = 0; col < cols; col++) {
//printf("getting col value\n");
const char * val = (const char*) sqlite3_column_text(stmt, col);
//printf("value ok\n");
jstring valStr;
if (val == NULL) {
//printf("value is null\n");
//free((void*) val);
//printf("creating value string \"\"\n");
valStr = (*env)->NewStringUTF(env, "");
//printf("create ok\n");
} else {
//printf("creating value string from %s\n", val);
valStr = (*env)->NewStringUTF(env, val);
//printf("create ok\n");
}
//printf("adding value to row object array %d/%d\n", col + 1, cols);
(*env)->SetObjectArrayElement(env, row, col, valStr);
//printf("add ok\n");
}
} else if (retval == SQLITE_DONE) {
break;
} else {
throwSqliteException(env, sqlite3_errmsg(db));
break;
}
}
// copy data from dynamic array to java's String[][]
if (da->elements > 0) {
//printf("creating object array table\n");
table = (*env)->NewObjectArray(env, da->elements, (*env)->GetObjectClass(env, row), 0);
//printf("create ok\n");
int i = 0;
for (; i < da->elements; i++) {
//printf("getting object from da\n");
jobjectArray item = *(jobjectArray *) da->array[i];
//printf("get ok\n");
//printf("adding object row to table\n");
(*env)->SetObjectArrayElement(env, table, i, item);
//free(da->array[i]);
//printf("add ok\n");
}
}
// free memory
//printf("freeing da memory\n");
dynamicArray_free(da);
//printf("free ok\n");
} while (strlen(leftOver));
}
// free memory
//printf("closing database\n");
//sqlite3_free(stmt);
sqlite3_close(db);
//printf("close ok\n");
//printf("releasing jdatabase\n");
(*env)->ReleaseStringUTFChars(env, database, jDatabase);
//printf("release ok\n");
//printf("releasing jsql\n");
(*env)->ReleaseStringUTFChars(env, sql, jSql);
//printf("release ok\n");
return table;
}
В основном функция открывает базу данных, выполняет запрос, создает из нее массив Java 2d и отправляет его обратно. База данных закрыта в конце.
Вот моя реализация динамического массива:
#include "dynamicArray.h"
#include <stdlib.h>
void dynamicArray_add(dynamicArray * da, void * item) {
if (da->elements == da->allocated) {
if (da->allocated == 0)
da->allocated = 3;
else
da->allocated *= 2;
void *_tmp = realloc(da->array, (da->allocated * sizeof (item)));
if (!_tmp) {
return;
}
da->array = (void**) _tmp;
}
da->array[da->elements] = item;
da->elements++;
}
dynamicArray * dynamicArray_init() {
dynamicArray * da = calloc(1, sizeof (dynamicArray));
return da;
}
void dynamicArray_free(dynamicArray * da) {
free(da->array);
free(da);
}
и тестовый код Java
public void testQueryLoop() throws Exception {
Sqlite.query("testna.db", "drop table if exists user");
Sqlite.query("testna.db", "create table user (username, password)");
Sqlite.query("testna.db", "insert into user values ('u1', 'a')");
Sqlite.query("testna.db", "insert into user values('u2', 'b1');");
for (int i = 0; i < 1010; i++) {
if (i % 500 == 0) {
System.out.println("gcing");
Runtime.getRuntime().gc();
}
System.out.println(i);
String[][] r = Sqlite.query("testna.db", "select * from user");
assertEquals(2, r.length);
assertEquals("u1", r[0][0]);
assertEquals("b1", r[1][1]);
}
}
(эта сборка мусора не помогает)
Сбой цикла после 1003 итераций.
Это ошибка, которую я получаю
#
# A fatal error has been detected by the Java Runtime Environment:
#
# SIGSEGV (0xb) at pc=0x01179d1a, pid=5114, tid=3079269232
#
# JRE version: 6.0_20-b20
# Java VM: OpenJDK Client VM (19.0-b09 mixed mode, sharing linux-x86 )
# Derivative: IcedTea6 1.9.4
# Distribution: Ubuntu 10.10, package 6b20-1.9.4-0ubuntu1
# Problematic frame:
# V [libjvm.so+0x21cd1a]
#
# Can not save log file, dump to screen..
#
# A fatal error has been detected by the Java Runtime Environment:
#
# SIGSEGV (0xb) at pc=0x01179d1a, pid=5114, tid=3079269232
#
# JRE version: 6.0_20-b20
# Java VM: OpenJDK Client VM (19.0-b09 mixed mode, sharing linux-x86 )
# Derivative: IcedTea6 1.9.4
# Distribution: Ubuntu 10.10, package 6b20-1.9.4-0ubuntu1
# Problematic frame:
# V [libjvm.so+0x21cd1a]
#
# If you would like to submit a bug report, please include
# instructions how to reproduce the bug and visit:
# https://bugs.launchpad.net/ubuntu/+source/openjdk-6/
#
--------------- T H R E A D ---------------
Current thread (0x09a9dc00): JavaThread "main" [_thread_in_vm, id=5116, stack(0xb784e000,0xb789f000)]
siginfo:si_signo=SIGSEGV: si_errno=0, si_code=1 (SEGV_MAPERR), si_addr=0x00000000
Registers:
EAX=0x00000000, EBX=0x013baff4, ECX=0x09a9e3d4, EDX=0x09a9dc00
ESP=0xb789d644, EBP=0xb789d6a8, ESI=0x09a9dc00, EDI=0x09a9dc00
EIP=0x01179d1a, CR2=0x00000000, EFLAGS=0x00210286
Register to memory mapping:
EAX=0x00000000
0x00000000 is pointing to unknown location
EBX=0x013baff4
0x013baff4: in /usr/lib/jvm/java-6-openjdk/jre/lib/i386/client/libjvm.so at 0x00f5d000
ECX=0x09a9e3d4
0x09a9e3d4 is pointing to unknown location
EDX=0x09a9dc00
"main" prio=10 tid=0x09a9dc00 nid=0x13fc runnable [0xb789d000]
java.lang.Thread.State: RUNNABLE
ESP=0xb789d644
0xb789d644 is pointing into the stack for thread: 0x09a9dc00
"main" prio=10 tid=0x09a9dc00 nid=0x13fc runnable [0xb789d000]
java.lang.Thread.State: RUNNABLE
EBP=0xb789d6a8
0xb789d6a8 is pointing into the stack for thread: 0x09a9dc00
"main" prio=10 tid=0x09a9dc00 nid=0x13fc runnable [0xb789d000]
java.lang.Thread.State: RUNNABLE
ESI=0x09a9dc00
"main" prio=10 tid=0x09a9dc00 nid=0x13fc runnable [0xb789d000]
java.lang.Thread.State: RUNNABLE
EDI=0x09a9dc00
"main" prio=10 tid=0x09a9dc00 nid=0x13fc runnable [0xb789d000]
java.lang.Thread.State: RUNNABLE
Top of Stack: (sp=0xb789d644)
0xb789d644: 09a9dc00 013d15e0 013d15e0 00000000
0xb789d654: 00000000 b789d66c 09a9e3d8 8fa9fa98
0xb789d664: b789d67c 00ea5238 09a9dc00 00000000
0xb789d674: ffffffff 00d29ff4 09a9dc00 09a9e3d0
0xb789d684: 00000088 013926a8 09a9dc00 00000000
0xb789d694: 00000001 00000005 00f28ff4 8fa9fa98
0xb789d6a4: 09a9dc00 b789d6d8 00ea8e49 09a9dd18
0xb789d6b4: 00000000 00f25b42 09a9dc00 b789d758
Instructions: (pc=0x01179d1a)
0x01179d0a: 52 89 75 e4 52 56 51 e8 6a be 00 00 58 8b 45 0c
0x01179d1a: 8b 38 57 e8 fe 7a fe ff 89 c7 83 c4 10 83 c0 08
Stack: [0xb784e000,0xb789f000], sp=0xb789d644, free space=317k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
V [libjvm.so+0x21cd1a]
C [libSqlite.so+0x4e49] throwSqliteException+0x4d
C [libSqlite.so+0x4f3f] Java_sqliteNative_Sqlite_query+0xbb
j sqliteNative.Sqlite.query(Ljava/lang/String;Ljava/lang/String;)[[Ljava/lang/String;+0
j sqliteNative.SqliteTest.testQueryLoop()V+74
v ~StubRoutines::call_stub
V [libjvm.so+0x203db2]
V [libjvm.so+0x302879]
V [libjvm.so+0x202d4f]
V [libjvm.so+0x33f164]
V [libjvm.so+0x33fdfe]
V [libjvm.so+0x258d01]
C [libjava.so+0x149c2] Java_sun_reflect_NativeMethodAccessorImpl_invoke0+0x32
j sun.reflect.NativeMethodAccessorImpl.invoke0(Ljava/lang/reflect/Method;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;+0
j sun.reflect.NativeMethodAccessorImpl.invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;+87
j sun.reflect.DelegatingMethodAccessorImpl.invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;+6
j java.lang.reflect.Method.invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;+161
j org.junit.runners.model.FrameworkMethod$1.runReflectiveCall()Ljava/lang/Object;+15
j org.junit.internal.runners.model.ReflectiveCallable.run()Ljava/lang/Object;+1
j org.junit.runners.model.FrameworkMethod.invokeExplosively(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;+10
j org.junit.internal.runners.statements.InvokeMethod.evaluate()V+12
j org.junit.runners.BlockJUnit4ClassRunner.runNotIgnored(Lorg/junit/runners/model/FrameworkMethod;Lorg/junit/internal/runners/model/EachTestNotifier;)V+9
j org.junit.runners.BlockJUnit4ClassRunner.runChild(Lorg/junit/runners/model/FrameworkMethod;Lorg/junit/runner/notification/RunNotifier;)V+28
j org.junit.runners.BlockJUnit4ClassRunner.runChild(Ljava/lang/Object;Lorg/junit/runner/notification/RunNotifier;)V+6
j org.junit.runners.ParentRunner$3.run()V+12
j org.junit.runners.ParentRunner$1.schedule(Ljava/lang/Runnable;)V+1
j org.junit.runners.ParentRunner.runChildren(Lorg/junit/runner/notification/RunNotifier;)V+40
j org.junit.runners.ParentRunner.access$000(Lorg/junit/runners/ParentRunner;Lorg/junit/runner/notification/RunNotifier;)V+2
j org.junit.runners.ParentRunner$2.evaluate()V+8
j org.junit.internal.runners.statements.RunBefores.evaluate()V+49
j org.junit.internal.runners.statements.RunAfters.evaluate()V+18
j org.junit.runners.ParentRunner.run(Lorg/junit/runner/notification/RunNotifier;)V+20
j junit.framework.JUnit4TestAdapter.run(Ljunit/framework/TestResult;)V+13
j org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.run()V+431
j org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.launch(Lorg/apache/tools/ant/taskdefs/optional/junit/JUnitTest;ZZZZZZ)I+39
j org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.main([Ljava/lang/String;)V+781
v ~StubRoutines::call_stub
V [libjvm.so+0x203db2]
V [libjvm.so+0x302879]
V [libjvm.so+0x202d4f]
V [libjvm.so+0x20d9f4]
V [libjvm.so+0x2277cd]
C [java+0x2ef5] JavaMain+0xd45
C [libpthread.so.0+0x5cc9]
Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)
j sqliteNative.Sqlite.query(Ljava/lang/String;Ljava/lang/String;)[[Ljava/lang/String;+0
j sqliteNative.SqliteTest.testQueryLoop()V+74
v ~StubRoutines::call_stub
j sun.reflect.NativeMethodAccessorImpl.invoke0(Ljava/lang/reflect/Method;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;+0
j sun.reflect.NativeMethodAccessorImpl.invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;+87
j sun.reflect.DelegatingMethodAccessorImpl.invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;+6
j java.lang.reflect.Method.invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;+161
j org.junit.runners.model.FrameworkMethod$1.runReflectiveCall()Ljava/lang/Object;+15
j org.junit.internal.runners.model.ReflectiveCallable.run()Ljava/lang/Object;+1
j org.junit.runners.model.FrameworkMethod.invokeExplosively(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;+10
j org.junit.internal.runners.statements.InvokeMethod.evaluate()V+12
j org.junit.runners.BlockJUnit4ClassRunner.runNotIgnored(Lorg/junit/runners/model/FrameworkMethod;Lorg/junit/internal/runners/model/EachTestNotifier;)V+9
j org.junit.runners.BlockJUnit4ClassRunner.runChild(Lorg/junit/runners/model/FrameworkMethod;Lorg/junit/runner/notification/RunNotifier;)V+28
j org.junit.runners.BlockJUnit4ClassRunner.runChild(Ljava/lang/Object;Lorg/junit/runner/notification/RunNotifier;)V+6
j org.junit.runners.ParentRunner$3.run()V+12
j org.junit.runners.ParentRunner$1.schedule(Ljava/lang/Runnable;)V+1
j org.junit.runners.ParentRunner.runChildren(Lorg/junit/runner/notification/RunNotifier;)V+40
j org.junit.runners.ParentRunner.access$000(Lorg/junit/runners/ParentRunner;Lorg/junit/runner/notification/RunNotifier;)V+2
j org.junit.runners.ParentRunner$2.evaluate()V+8
j org.junit.internal.runners.statements.RunBefores.evaluate()V+49
j org.junit.internal.runners.statements.RunAfters.evaluate()V+18
j org.junit.runners.ParentRunner.run(Lorg/junit/runner/notification/RunNotifier;)V+20
j junit.framework.JUnit4TestAdapter.run(Ljunit/framework/TestResult;)V+13
j org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.run()V+431
j org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.launch(Lorg/apache/tools/ant/taskdefs/optional/junit/JUnitTest;ZZZZZZ)I+39
j org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.main([Ljava/lang/String;)V+781
v ~StubRoutines::call_stub
--------------- P R O C E S S ---------------
Java Threads: ( => current thread )
0x09ad9000 JavaThread "Low Memory Detector" daemon [_thread_blocked, id=5123, stack(0xb4fc3000,0xb5014000)]
0x09ad7400 JavaThread "CompilerThread0" daemon [_thread_blocked, id=5122, stack(0xb5014000,0xb5095000)]
0x09ad5800 JavaThread "Signal Dispatcher" daemon [_thread_blocked, id=5121, stack(0xb5095000,0xb50e6000)]
0x09acdc00 JavaThread "Finalizer" daemon [_thread_blocked, id=5120, stack(0xb52e6000,0xb5337000)]
0x09acc400 JavaThread "Reference Handler" daemon [_thread_blocked, id=5119, stack(0xb5337000,0xb5388000)]
=>0x09a9dc00 JavaThread "main" [_thread_in_vm, id=5116, stack(0xb784e000,0xb789f000)]
Other Threads:
0x09acac00 VMThread [stack: 0xb5388000,0xb5409000] [id=5118]
0x09ae5000 WatcherThread [stack: 0xb4f42000,0xb4fc3000] [id=5124]
VM state:not at safepoint (normal execution)
VM Mutex/Monitor currently owned by a thread: None
Heap
def new generation total 4928K, used 83K [0x70310000, 0x70860000, 0x7aa60000)
eden space 4416K, 1% used [0x70310000, 0x70324dd0, 0x70760000)
from space 512K, 0% used [0x70760000, 0x70760000, 0x707e0000)
to space 512K, 0% used [0x707e0000, 0x707e0000, 0x70860000)
tenured generation total 10752K, used 579K [0x7aa60000, 0x7b4e0000, 0x8f910000)
the space 10752K, 5% used [0x7aa60000, 0x7aaf0cf0, 0x7aaf0e00, 0x7b4e0000)
compacting perm gen total 12288K, used 1605K [0x8f910000, 0x90510000, 0x93910000)
the space 12288K, 13% used [0x8f910000, 0x8faa1760, 0x8faa1800, 0x90510000)
ro space 10240K, 73% used [0x93910000, 0x94066d50, 0x94066e00, 0x94310000)
rw space 12288K, 60% used [0x94310000, 0x94a4f9f8, 0x94a4fa00, 0x94f10000)
Dynamic libraries:
Can not get library information for pid = 5116
VM Arguments:
jvm_args: -Djava.library.path=/home/vojko/NetBeansProjects/Sqlite/dist/Debug/GNU-Linux-x86/
java_command: org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner sqliteNative.SqliteTest filtertrace=true haltOnError=false haltOnFailure=false showoutput=true outputtoformatters=true logfailedtests=true logtestlistenerevents=true formatter=org.apache.tools.ant.taskdefs.optional.junit.BriefJUnitResultFormatter formatter=org.apache.tools.ant.taskdefs.optional.junit.XMLJUnitResultFormatter,/home/vojko/NetBeansProjects/sqliteNative/build/test/results/TEST-sqliteNative.SqliteTest.xml crashfile=/home/vojko/NetBeansProjects/sqliteNative/build/junitvmwatcher6420726485344985293.properties propsfile=/home/vojko/NetBeansProjects/sqliteNative/build/junit7069577112414567858.properties
Launcher Type: SUN_STANDARD
Environment Variables:
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games
USERNAME=vojko
LD_LIBRARY_PATH=/usr/lib/jvm/java-6-openjdk/jre/lib/i386/client:/usr/lib/jvm/java-6-openjdk/jre/lib/i386:/usr/lib/jvm/java-6-openjdk/jre/../lib/i386
SHELL=/bin/bash
DISPLAY=:0.0
Signal Handlers:
SIGSEGV: [libjvm.so+0x3df4f0], sa_mask[0]=0x7ffbfeff, sa_flags=0x10000004
SIGBUS: [libjvm.so+0x3df4f0], sa_mask[0]=0x7ffbfeff, sa_flags=0x10000004
SIGFPE: [libjvm.so+0x3055c0], sa_mask[0]=0x7ffbfeff, sa_flags=0x10000004
SIGPIPE: [libjvm.so+0x3055c0], sa_mask[0]=0x7ffbfeff, sa_flags=0x10000004
SIGXFSZ: [libjvm.so+0x3055c0], sa_mask[0]=0x7ffbfeff, sa_flags=0x10000004
SIGILL: [libjvm.so+0x3055c0], sa_mask[0]=0x7ffbfeff, sa_flags=0x10000004
SIGUSR1: SIG_DFL, sa_mask[0]=0x00000000, sa_flags=0x00000000
SIGUSR2: [libjvm.so+0x304c70], sa_mask[0]=0x00000004, sa_flags=0x10000004
SIGHUP: [libjvm.so+0x3077e0], sa_mask[0]=0x7ffbfeff, sa_flags=0x10000004
SIGINT: SIG_IGN, sa_mask[0]=0x00000000, sa_flags=0x00000000
SIGTERM: [libjvm.so+0x3077e0], sa_mask[0]=0x7ffbfeff, sa_flags=0x10000004
SIGQUIT: [libjvm.so+0x3077e0], sa_mask[0]=0x7ffbfeff, sa_flags=0x10000004
--------------- S Y S T E M ---------------
OS:Linux
uname:Linux 2.6.35-25-generic #44-Ubuntu SMP Fri Jan 21 17:40:48 UTC 2011 i686
libc:glibc 2.12.1 NPTL 2.12.1
rlimit: STACK 8192k, CORE 0k, NPROC infinity, NOFILE 1024, AS infinity
load average:0.00 0.00 0.00
/proc/meminfo:
CPU:total 1 (1 cores per cpu, 1 threads per core) family 6 model 37 stepping 2, cmov, cx8, fxsr, mmx, sse, sse2, sse3, ssse3, sse4.1, sse4.2, popcnt
Memory: 4k page, physical 1025708k(42264k free), swap 916476k(902956k free)
vm_info: OpenJDK Client VM (19.0-b09) for linux-x86 JRE (1.6.0_20-b20), built on Jan 7 2011 07:24:55 by "buildd" with gcc 4.4.5
time: Thu Feb 3 04:41:48 2011
elapsed time: 1 seconds
#
# If you would like to submit a bug report, please include
# instructions how to reproduce the bug and visit:
# https://bugs.launchpad.net/ubuntu/+source/openjdk-6/
#
Testsuite: sqliteNative.SqliteTest
Tests run: 1, Failures: 0, Errors: 1, Time elapsed: 0 sec
Testcase: sqliteNative.SqliteTest:testQueryLoop: Caused an ERROR
Forked Java VM exited abnormally. Please note the time in the report does not reflect the time until the VM exit.
junit.framework.AssertionFailedError: Forked Java VM exited abnormally. Please note the time in the report does not reflect the time until the VM exit.
at org.netbeans.core.execution.RunClassThread.run(Unknown Source)
У кого-нибудь есть предложения?
Спасибо