The same nestedvm/sqlite3.mips emaulated binary runs without error
on Linux. Strangley, it also runs fine on Windows when the database
file is on a LAN(!!!). It also runs fine on Windows in a :memory: db.
There appears to be some subtle UNIX-filesystem-emulation-in-Java
incompatability when running on a Windows local hard drive.
I have yet to isolate what exactly this incompatability is.
It's probably some crazy Windows implicit file-locking thing.
In any event, the lack of a correctly working fsync() did not appear
to impede the sqlite3.mips working in Linux or in Windows on a LAN.
Any ideas welcome.
--- Joe Wilson <deve...@yahoo.com> wrote:
> Hi, I thought I'd ask on this list before I bother anyone on the
> SQLite list in case this problem is NestedVM-specific.
> At first I thought it as due to the lack of fsync(), but now
> I am not so sure.
>
> I'm building sqlite3 from the unmodified SQLite 3.3.9 source release
> and compiling it to sqlite3.mips and then running it on Windows
> with the NestedVM interpreter.
>
> Given the file "stuff.sql" (at the bottom of this email) is piped
> to a NestedVM-interpreted version of sqlite3 I see the following error:
>
> nestedvm$ java -cp 'build;upstream/build/classgen/build' org.ibex.nestedvm.Interpreter
> sqlite3.mips -batch empty.db < stuff.sql
> fsync(4) called, but not implemented
> fsync(5) called, but not implemented
> fsync(4) called, but not implemented
> fsync(3) called, but not implemented
> fsync(4) called, but not implemented
> fsync(5) called, but not implemented
> fsync(4) called, but not implemented
> fsync(3) called, but not implemented
> fsync(4) called, but not implemented
> fsync(5) called, but not implemented
> fsync(4) called, but not implemented
> fsync(3) called, but not implemented
> SQL error near line 47: database disk image is malformed
> $ echo $?
> 1
>
> You can see that it produces a corrupt sqlite3 database file.
>
> However, there is no problem if the nested-interpreted sqlite3.mips binary
> is fed to a :memory: database:
>
> $ java -cp 'build;upstream/build/classgen/build' org.ibex.nestedvm.Interpreter sqlite3.mips
> -batch ':memory:' < stuff.sql
> $ echo $?
> 0
>
> Also, there is no problem if the native Windows sqlite3 commandline
> shell is run:
>
> sqlite3.exe -batch dummy.db < stuff.sql
>
> I used the following flags to compile sqlite3 under NestedVM:
>
> mips-unknown-elf-gcc \
> -O2 -mmemcpy -ffunction-sections -fdata-sections \
> -falign-functions=512 -fno-rename-registers \
> -fno-schedule-insns -fno-delayed-branch -freduce-all-givs \
> -march=mips1 -DOS_UNIX=1 -DNDEBUG=1 \
> -DSQLITE_OMIT_VIRTUALTABLE=1 -DSQLITE_OMIT_SHARED_CACHE=1 \
> -DSQLITE_OMIT_LOAD_EXTENSION=1 -DTHREADSAFE=0 -DHAVE_USLEEP=1
>
> I'm trying to determine if this a NestedVM bug or a
> NestedVM-under-Windows bug or an sqlite3 bug.
>
> I don't have a UNIX box handy - I wonder what the sqlite 3.3.9
> shell would do under UNIX/LINUX with the following command:
>
> sqlite3 -batch zero_length_file.db < stuff.sql
>
> Any thoughts?
>
>
> stuff.sql:
>
> CREATE TABLE SOMETHING_ABC (
> ABC_DEF_GHIJ TEXT,
> SURE TEXT,
> BIRDFEEDER TEXT,
> NOTHING TEXT,
> SANDWICH TEXT,
> TURKEY TEXT,
> RESTING TEXT,
> RULERS TEXT,
> CUSTOM TEXT,
> ANOTHER TEXT,
> BLASTOFF TEXT,
> OFCOURSE TEXT,
> DESCRIPTION TEXT,
> WHENEVER TEXT,
> PRIMARY KEY (ABC_DEF_GHIJ, BIRDFEEDER, SANDWICH, SURE)
> );
> CREATE TABLE ANOTHER_TABLE (
> ABC_DEF_GHIJ TEXT,
> POSITION TEXT,
> HOUSENUM TEXT,
> LASTTIME TEXT,
> PRIMARY KEY (ABC_DEF_GHIJ, POSITION, HOUSENUM)
> );
> CREATE TABLE ONE_MORE_TABLE (
> ABC_DEF_GHIJ TEXT,
> CATS TEXT,
> BIRDFEEDER TEXT,
> BONUS_POOLS TEXT,
> PAINTER TEXT,
> CAR TEXT,
> NO_DOG TEXT,
> ABC_CODE TEXT,
> FRONTS TEXT,
> NOTHING TEXT,
> SANDWICH TEXT,
> ITEMNUM TEXT,
> SOLUTION TEXT,
> HOMEDIR TEXT,
> LIGHT_COL TEXT,
> MODEM_NUMBER TEXT,
> MODEM_PLACE TEXT,
> DESCRIPTION TEXT,
> LASTTIME TEXT,
> PRIMARY KEY (ABC_DEF_GHIJ, BIRDFEEDER, CAR, ABC_CODE)
> );
> CREATE TABLE STILL_ANOTHER_TABLE (
> PLACES TEXT,
> FRONTS TEXT,
> MAIN_SANDWICH TEXT,
> ABC_CODE TEXT,
> CAR TEXT,
> BIRDFEEDER TEXT,
> NOTHING TEXT,
> SANDWICH TEXT,
> ITEMNUM TEXT,
> SOLUTION TEXT,
> HOMEDIR TEXT,
> LIGHT_COL TEXT,
> DESCRIPTION TEXT,
> PRIMARY KEY (PLACES, FRONTS, MAIN_SANDWICH, ABC_CODE, CAR)
> );
__________________________________________________
Do You Yahoo!?
Tired of spam? Yahoo! Mail has the best spam protection around
http://mail.yahoo.com
I've found the cause of the sqlite3 database corruption in NestedVM.
The current NestedVM sys_fstat() implementation caches the old file
length() in certain circumstances. This can cause a lot of trouble
for SQLite database integrity, as you can imagine.
The attached patch avoids file length caching in fstat() (in JDK 1.4.2)
and allows the test case to successfully run to completion and
produce a database file with the correct md5sum.
There may be other filesystem-related attributes that are also cached
in NestedVM. It may take a while to track them all down.
If I can port Tcl to NestedVM, perhaps I can run the sqlite test suite.
This would certainly help find any other potential NestedVM filesystem
problems.
Please hold off on applying this patch until the original test case
is run with a working fsync(). Thanks.
> I've found the cause of the sqlite3 database corruption in NestedVM.
> The current NestedVM sys_fstat() implementation caches the old file
> length() in certain circumstances. This can cause a lot of trouble
> for SQLite database integrity, as you can imagine.
>
> The attached patch avoids file length caching in fstat() (in JDK 1.4.2)
> and allows the test case to successfully run to completion and
> produce a database file with the correct md5sum.
I put up version 2007-01-07, which has a working fsync() in it.
d
The tar file nestedvm-2007-01-07.tgz contains the old no-op fsync() in
nestedvm-2007-01-07/src/org/ibex/nestedvm/support_aux.c.
This led to some confusion when I tried to put a println() in the fsync
implementation to prove it was being called on Windows and found it
wasn't in Runtime.java. However, I do see your fsync fix in the tar
file under:
_darcs/patches/20070107161020-0c629-f8cb28cb78260e588b7a1a5515d6a3a6c940a7f0.gz
Once the fsync patch was manually applied, the sqlite test on Windows
ran to completion without database corruption.
(offline if someone could tell me how to update my local src tree
in darcs from a local repository, I'd appreciate it)
While the earlier HostFStat/size patch is not needed in this case,
it might be worth considering applying if only to make NestedVM
on Windows match Linux/UNIX's behavior in the absence of fsync.
I don't know what POSIX says about fstat() behavior in relation
to not being fsync'd, but UNIXes don't seem to need it in this
particular case.
Looking forward an ACID-compliant sqlite-3.3.10 version of your
jdbc driver.
thanks.
____________________________________________________________________________________
Do you Yahoo!?
Everyone is raving about the all-new Yahoo! Mail beta.
http://new.mail.yahoo.com
I'm not sure I understand what is going on here. NestedVM does cache
the fstat object (and the java.io.File that goes with it) but nothing
along the way should be caching the data returned by File.length(). Do
RandomAccessFile.length() and File.length() actually return different
values under certain conditions? Could you try running this program
under the different configurations you're using?
http://www.brianweb.net/misc/Test.java
I get the expected values for both f.length() and raf.length() on OS X,
FreeBSD, XP writing to Samba share on FreeBSD and XP writing to a local
NTFS filesystem. Maybe I'm just not doing the right combination of file
operations in my test app though.
-Brian
RandomAccessFile.length() and File.length() will return different
values for Java 1.4.2 under Windows 2000 and XP on a local NTFS drive
under certain conditions.
$ uname
CYGWIN_NT-5.0
$ pwd
/cygdrive/c
$ java -version
java version "1.4.2_06"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.2_06-b03)
Java HotSpot(TM) Client VM (build 1.4.2_06-b03, mixed mode)
$ java -cp . Test
After create (should be 0): f.length(): 0 raf.length(): 0
After write (should be 4): f.length(): 0 raf.length(): 4
After setLength(2) (should be 2): f.length(): 2 raf.length(): 2
After setLength(0) (should be 0): f.length(): 0 raf.length(): 0
After seek(8) (should be 0): f.length(): 0 raf.length(): 0
After write (should be 12): f.length(): 0 raf.length(): 12
I don't have an XP box here, but when I ran a similar test on XP
on a local drive yesterday it produced the same type of results.
In Solaris and Linux, RandomAccessFile.length() and File.length()
always appear to return the same values under the conditions I've
(previsouly) tried. Oddly, RandomAccessFile.length() and File.length()
will also return the same value for Windows XP on a network share.
I do not have access to a LAN, Linux or Solaris at the moment, so
I can't run your program in those environments. But I think the output
of the test case above is sufficient to establish the problem on
a common platform.
____________________________________________________________________________________
We won't tell. Get more on shows you hate to love
(and love to hate): Yahoo! TV's Guilty Pleasures list.
http://tv.yahoo.com/collections/265
Interesting. I think this is definitely a bug in the JDK. Your
fix would certainly work. Having stat and fstat not return the same
results makes me a little uneasy though. I'm not sure what posix says
about it but it doesn't seem right. However, making stat check for open
files first would be really ugly, and still wouldn't fix the problem if
another process modifies the file.
I'll dig around some more and see if I can find some other way to work
around the bug.
Thanks a lot for tracking this down. I'm sure it wasn't easy when all
you had to go on was a corrupt file. There are about a billion places
things could've gone wrong to cause that.
-Brian
Oops. I have uploaded a new version which seems to have the patch.
> (offline if someone could tell me how to update my local src tree
> in darcs from a local repository, I'd appreciate it)
If you have darcs installed, you just pull or push another repository. Eg.
$ ls
nestedvm-new
nestedvm-cur
$ cd nestedvm-new
$ darcs pull ../nestedvm-cur
You can also do things like:
$ darcs pull http://nestedvm.ibex.org
or
$ darcs pull http://zentus.com/sqlitejdbc/src
> Looking forward an ACID-compliant sqlite-3.3.10 version of your
> jdbc driver.
I held back with 3.3.9 because of the data-corruption mess, and I
haven't made a new release yet because something funny has happened to
my UTF tests. Either my understanding of UTF-16 is wrong, and thus my
test is bad, or something was broken. (The error is probably mine.)
Anyone know of some complex, valid UTF-16 strings?
d
I think it would be pretty hard to screw up. The only thing you have to
worry about are surrogate pairs and you don't even have to worry about
them if you're converting to/from java Strings as they're stored in
UTF16.
-Brian
Yeah, but that means something very subtle has changed in the SQLite
API between 3.3.8 and 3.3.9. That's far more work to fix than if I
just made a mistake. :)
It will have to wait until Monday, I am off to Canada tonight.
d
What happens if you revert this sqlite utf.c patch?
http://www.sqlite.org/cvstrac/chngview?cn=3479
____________________________________________________________________________________
No need to miss a message. Get email on-the-go
with Yahoo! Mail for Mobile. Get started.
http://mobile.yahoo.com/mail
Why not use the HostFStat file length patch I supplied? It is
no less efficient than the current NestedVM file length implementation
in that it does not create any new objects; it merely references
RandomAccessFile objects that have already been created.
File.length() is simply not reliable on Windows on Sun's 1.4.x JDK
unless you call raf.getFD().sync() before each of its invocations,
which is both grossly inefficient and requires a RandomAccessFile
object anyway (to get a FileDescriptor object to call sync()).
Using RandomAccessFile.length() is the only reliable option.
As for my earlier comment that JDK 1.4.x File.length() was reliable
on Windows 2000/XP on a LAN - I was mistaken:
WINXP$ uname
CYGWIN_NT-5.1
WINXP$ java -version
java version "1.4.2_08"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.2_08-b03)
Java HotSpot(TM) Client VM (build 1.4.2_08-b03, mixed mode)
WINXP$ java -cp . Test
After create (should be 0): f.length(): 0 raf.length(): 0
After write (should be 4): f.length(): 1 raf.length(): 4
After setLength(2) (should be 2): f.length(): 2 raf.length(): 2
After setLength(0) (should be 0): f.length(): 0 raf.length(): 0
After seek(8) (should be 0): f.length(): 0 raf.length(): 0
After write (should be 12): f.length(): 9 raf.length(): 12
File.length() on Java 1.4.x on Windows on a LAN produces different
results from that of a local drive - but it is still incorrect.
> Thanks a lot for tracking this down. I'm sure it wasn't easy when all
> you had to go on was a corrupt file. There are about a billion places
> things could've gone wrong to cause that.
It was time consuming, but not difficult.
Because the MIPS emulation is a 100% deterministic in-memory operation
and the emulated program ran fine on UNIX and not in Windows I suspected
from the start that the problem had to be I/O related. It was just a matter
of putting a few dozen printfs in the sqlite I/O functions and seeing
where a known good process' output first differed from that of the corrupt
process.
Too bad Windows has no equivalent of strace or truss for non-Cygwin
programs such as Sun's JVM. Then the task would have been much easier.
Gee, I guess I could have used GCJ to run NestedVM to run the sqlite
mips binary on Windows with strace... but then the universe would
have collapsed in upon itself. Next time, maybe.
____________________________________________________________________________________
Get your own web address.
Have a HUGE year through Yahoo! Small Business.
http://smallbusiness.yahoo.com/domains/?p=BESTDEAL
Whoa... I never really thought about the security implications of
accepting overlong UTF8 encodings. I've gotta fix my UTF8 decoder
(nothing in NestedVM, don't worry).
Looks like that patch makes the decoder a little more strict about
accepting invalid encodings. I bet you have some invalid UTF8/UTF16 in
your tests.
-Brian
I probably will. There is nothing wrong with the efficiency of the
patch. The only part that bugs me is that:
int fd = open("foo",O_RDONLY);
fstat(fd,&statbuf);
now wouldn't do the same thing as
stat("foo",&statbuf);
since the first would be checking RandomAccessFile.length() and the
second File.length(). If your application happens to use stat instead
of fstat things fall apart again.
> After create (should be 0): f.length(): 0 raf.length(): 0
> After write (should be 4): f.length(): 1 raf.length(): 4
> After setLength(2) (should be 2): f.length(): 2 raf.length(): 2
> After setLength(0) (should be 0): f.length(): 0 raf.length(): 0
> After seek(8) (should be 0): f.length(): 0 raf.length(): 0
> After write (should be 12): f.length(): 9 raf.length(): 12
Even more interesting. It is like is sees part of the write but not the
whole thing. If you stick a sleep call just before the printlns do by
chance get different results?
> Gee, I guess I could have used GCJ to run NestedVM to run the sqlite
It isn't so crazy. We actually run NestedVM under GCJ in XWT/Ibex. The
world definitely started to crumble a little, but as far as I know it
hasn't collapsed on itself yet.
-Brian
I think I may have just answered my own question.
http://209.85.165.104/search?q=cache:j5h4qGrKKiMJ:bugs.sun.com/bugdatabase/view_bug.do%3Fbug_id%3D4266941+java.io.file+length+site:bugs.sun.com&hl=en&gl=us&ct=clnk&cd=64&client=firefox-a
(Looks like sun's bug database is down, had to use google's cache).
It appears that writes just plain don't happen until a little later
under Windows on JDK < 1.5. This makes me even more hesitant to work
around the bug by using RandomAccessFile.length(). The bug seems to be
in the file writing, not java.io.File.
getFD().sync() will work around it, but as you pointed out that is way
too expensive to do after every write. I'm going to try to find as box
that still has jdk1.4 on it (or download it again) and see if I can
figure out some more.
-Brian
I see. Maybe always create a RandomAccessFile object for each File
object? I haven't looked at Classpath or the newly GPL'd SUN Java
implementation of RandomAccessFile to see if it is a heavy-weight
object - I suspect it is just a lightweight wrapper around a single
int file descriptor.
> > After create (should be 0): f.length(): 0 raf.length(): 0
> > After write (should be 4): f.length(): 1 raf.length(): 4
> > After setLength(2) (should be 2): f.length(): 2 raf.length(): 2
> > After setLength(0) (should be 0): f.length(): 0 raf.length(): 0
> > After seek(8) (should be 0): f.length(): 0 raf.length(): 0
> > After write (should be 12): f.length(): 9 raf.length(): 12
>
> Even more interesting. It is like is sees part of the write but not the
> whole thing. If you stick a sleep call just before the printlns do by
> chance get different results?
You get random results depending on how fast the CPU is, the OS settings
and how loaded the machine is. Below are a number of tests with args[0]
in milliseconds:
WIN2K$ java -cp . TestWithSleep 900
After create (should be 0): f.length(): 0 raf.length(): 0
After write (should be 4): f.length(): 0 raf.length(): 4
After setLength(2) (should be 2): f.length(): 2 raf.length(): 2
After setLength(0) (should be 0): f.length(): 0 raf.length(): 0
After seek(8) (should be 0): f.length(): 0 raf.length(): 0
After write (should be 12): f.length(): 12 raf.length(): 12
WIN2K$ java -cp . TestWithSleep 800
After create (should be 0): f.length(): 0 raf.length(): 0
After write (should be 4): f.length(): 0 raf.length(): 4
After setLength(2) (should be 2): f.length(): 2 raf.length(): 2
After setLength(0) (should be 0): f.length(): 0 raf.length(): 0
After seek(8) (should be 0): f.length(): 0 raf.length(): 0
After write (should be 12): f.length(): 0 raf.length(): 12
WIN2K$ java -cp . TestWithSleep 800
After create (should be 0): f.length(): 0 raf.length(): 0
After write (should be 4): f.length(): 0 raf.length(): 4
After setLength(2) (should be 2): f.length(): 2 raf.length(): 2
After setLength(0) (should be 0): f.length(): 0 raf.length(): 0
After seek(8) (should be 0): f.length(): 0 raf.length(): 0
After write (should be 12): f.length(): 12 raf.length(): 12
Calling raf.getFD().sync() before each print always yields the
correct result in Windows JDK 1.4.2.x, but at the cost of slowing down
the application with unnecessary disk writes.
> > Gee, I guess I could have used GCJ to run NestedVM to run the sqlite
>
> It isn't so crazy. We actually run NestedVM under GCJ in XWT/Ibex. The
> world definitely started to crumble a little, but as far as I know it
> hasn't collapsed on itself yet.
GCJ is great. I just was referring to all the layers of emulation/runtime
indirection hurting my brain.
I now realize that GCJ/Classpath would not have helped debug the problem
in SUN JDK 1.4.x on Windows. The JDK source would have to be compiled
using GNU libc under Cygwin for strace to work.
____________________________________________________________________________________
Bored stiff? Loosen up...
Download and play hundreds of games for free on Yahoo! Games.
http://games.yahoo.com/games/front
A couple of more data points below. It takes 2200ms to yield
the correct results in all cases - but even then it is not
reliable:
WIN2K$ java -cp . TestWithSleep 2200
After create (should be 0): f.length(): 0 raf.length(): 0
After write (should be 4): f.length(): 4 raf.length(): 4 <===
After setLength(2) (should be 2): f.length(): 2 raf.length(): 2
After setLength(0) (should be 0): f.length(): 0 raf.length(): 0
After seek(8) (should be 0): f.length(): 0 raf.length(): 0
After write (should be 12): f.length(): 12 raf.length(): 12
WIN2K$ java -cp . TestWithSleep 2200
After create (should be 0): f.length(): 0 raf.length(): 0
After write (should be 4): f.length(): 0 raf.length(): 4 <===
After setLength(2) (should be 2): f.length(): 2 raf.length(): 2
After setLength(0) (should be 0): f.length(): 0 raf.length(): 0
After seek(8) (should be 0): f.length(): 0 raf.length(): 0
After write (should be 12): f.length(): 12 raf.length(): 12
import java.io.*;
public class TestWithSleep {
static int _ms = 1;
static void sleep() {try {java.lang.Thread.sleep(_ms);}catch(Throwable th){}}
public static void main(String[] args) throws IOException {
_ms = Integer.parseInt(args[0]);
File f = new File("foo");
f.delete();
RandomAccessFile raf = new RandomAccessFile("foo","rw");
System.out.println("After create (should be 0): f.length(): " +
f.length() + " raf.length(): " + raf.length());
raf.writeInt(0);
sleep();
System.out.println("After write (should be 4): f.length(): " +
f.length() + " raf.length(): " + raf.length());
raf.setLength(2);
sleep();
System.out.println("After setLength(2) (should be 2): f.length(): "
+ f.length() + " raf.length(): " + raf.length());
raf.setLength(0);
sleep();
System.out.println("After setLength(0) (should be 0): f.length(): "
+ f.length() + " raf.length(): " + raf.length());
raf.seek(8);
sleep();
System.out.println("After seek(8) (should be 0): f.length(): " +
f.length() + " raf.length(): " + raf.length());
raf.writeInt(0);
sleep();
System.out.println("After write (should be 12): f.length(): " +
f.length() + " raf.length(): " + raf.length());
}
}
____________________________________________________________________________________
Don't get soaked. Take a quick peak at the forecast
with the Yahoo! Search weather shortcut.
http://tools.search.yahoo.com/shortcuts/#loc_weather
RandomAccessFile's writing, length and syncing operations appear
to work as in UNIX. I think the SUN JDK < 1.5 bug is in their buffering
implementation for their File class and its lack of (timely) consistancy
with RandomAccessFile when working on the same file descriptor
"behind its back". So the answer seems to simply not use the java.io.File
class wherever possible and try to use RandomAccessFile methods instead.
RandomAccessFile's write methods seem to be timely and self-consistant
with length().
From a MIPS/UNIX syscall point of view NestedVM should not be buffering
writes anyway - newlib libc takes care of buffering in its own
implementation of fopen, fprintf, etc, does it not?
____________________________________________________________________________________
Sucker-punch spam with award-winning protection.
Try the free Yahoo! Mail Beta.
http://advision.webevents.yahoo.com/mailbeta/features_spam.html