Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
Grazing Occultation
Aug 18, 2009

by angerbutt

lemonadesweetheart posted:

It's pretty hard to determine what the problem is without the source. I know you said you tested for dynamic allocations so the only other place I can thing of is have you checked the type conversions, floats are tricky bitches. Also make sure that the type definitions are correct for the platforms you're using them on.

That's the thing. There are no type conversions taking place. It's literally just a line like this returning NaN:

double x = atan2(a, b);

Where a and b are both valid angles.

If I repeat that calculation it doesn't fail, presumably because I've changed the program enough to bump it away from whatever odd luck caused it.

I'm not sure if anyone here can solve it. I was just hoping that someone would say "that's a weird problem; maybe it's X?" and then I'd have a starting point. :( It's the sort of hard-to-repeat oddity that makes me immediately think memory corruption but I can't find any.

ctz posted:

Do you have any code or libraries which are using mmx/sse* functions without issuing emms?

Nope. This is all dead simple C. There's nothing the slightest bit more exotic than DLL exports.

Adbot
ADBOT LOVES YOU

covener
Jan 10, 2004

You know, for kids!

Grazing Occultation posted:

I've checked for memory corruption using _CRTDBG_CHECK_ALWAYS_DF because when I have a problem this odd it's usually something along those lines. I've also tested for memory leaks. That ruled out corruption from dynamically allocated memory and there aren't many statically allocated arrays.

Rational Purify is a decent "hail mary" option if you have access to it.

schnarf
Jun 1, 2002
I WIN.

Grazing Occultation posted:

That's the thing. There are no type conversions taking place. It's literally just a line like this returning NaN:

double x = atan2(a, b);

Where a and b are both valid angles.

If I repeat that calculation it doesn't fail, presumably because I've changed the program enough to bump it away from whatever odd luck caused it.

I'm not sure if anyone here can solve it. I was just hoping that someone would say "that's a weird problem; maybe it's X?" and then I'd have a starting point. :( It's the sort of hard-to-repeat oddity that makes me immediately think memory corruption but I can't find any.


Nope. This is all dead simple C. There's nothing the slightest bit more exotic than DLL exports.
You could try making a wrapper function for atan2 that does something like dumping the floating point registers, calls atan2(), and then breaks if it returned an unexpected value, and calling that wrapper instead to debug the problem.

Contero
Mar 28, 2004

code:
#include <cstdio>

struct also {
   void go() { printf("thing\n"); }
};

struct again {
   void go2() { printf("my\n"); }
};

struct bla : public also, public again { 
   float thing;
};

int main()
{
   printf("%d\n", sizeof(bla));

   return 0;
}
In visual studio 2008, this outputs 8. If I remove either one of the base classes it changes back to 4. gcc handles this just fine and outputs 4. What the hell is going on and how do I fix it? I'd rather not remove multiple inheritance.

EDIT:

Well I kind of solved it by doing this:

code:
#include <cstdio>

struct A {
   void a() { printf("A\n"); }
};

template <typename T>
struct B : public T {
   void b() { printf("B\n"); }
};

struct C : public B<A> {
   float c;
};

int main()
{
   printf("%d\n", sizeof(C));

   return 0;
}

Contero fucked around with this message at 08:21 on Sep 26, 2010

rjmccall
Sep 7, 2007

no worries friend
Fun Shoe
The Visual Studio C++ ABI's class layout algorithm is really wonky when it comes to empty base classes. I believe it is trying to make sure that the base subobjects have different addresses, even though they don't share any common base types.

Grazing Occultation
Aug 18, 2009

by angerbutt

covener posted:

Rational Purify is a decent "hail mary" option if you have access to it.

Unfortunately the only copy we have is from 2001 and no one knows where the CD is any more!

schnarf posted:

You could try making a wrapper function for atan2 that does something like dumping the floating point registers, calls atan2(), and then breaks if it returned an unexpected value, and calling that wrapper instead to debug the problem.

This is a good idea. I might give that a shot - even if the problem is somewhere else, maybe it'll give me more clues.

schnarf
Jun 1, 2002
I WIN.

Grazing Occultation posted:

This is a good idea. I might give that a shot - even if the problem is somewhere else, maybe it'll give me more clues.
Ahh, one other thought, and sorry for how vague it is. If I remember right, some of my coworkers ran into some mysterious bug that resulted from something to do with calling conventions. It was something like, there was some function that had the wrong calling convention (possibly it was that the prototype didn't have the correct calling convention), and that function was passed/returned floats. As a result, the floating point stack would occasionally end up in a weird state. You could check to see if anything along these [extremely vague] lines applies to your case. I'll try to remember to ask them about that on Monday.

StickFigs
Sep 5, 2004

"It's time to choose."
I'm trying to use the Eclipse IDE for C++ with cygwin and I can't figure out how to make it work. When I try to build the .exe I get this message in the console:

code:
**** Build of configuration Release for project Test2 ****

make all 
make: Nothing to be done for `all'.
And the .exe won't build, this is followed by a blank console when I try to run the program in eclipse (It's just a "Hello World!" thing)

I'm using Windows 7 32-bit.

Grazing Occultation
Aug 18, 2009

by angerbutt

schnarf posted:

Ahh, one other thought, and sorry for how vague it is. If I remember right, some of my coworkers ran into some mysterious bug that resulted from something to do with calling conventions. It was something like, there was some function that had the wrong calling convention (possibly it was that the prototype didn't have the correct calling convention), and that function was passed/returned floats. As a result, the floating point stack would occasionally end up in a weird state. You could check to see if anything along these [extremely vague] lines applies to your case. I'll try to remember to ask them about that on Monday.

It's all using doubles and the default calling convention. The functions in question aren't even DLL-exported. I could handle getting this sort of weird voodoo poo poo if I had the balls to do things like that! :v:

I've noticed two really strange things:

If I put only one trig call on a line then the problem does not occur. For example:
code:
double a1 = cos(PI/2-lat1);
double a2 = cos(PI/2- lat2);
double a = a1 * a2;
double b = sin(PI/2-lat1) * sin(PI/2- lat2) * cos(lon2 - lon1);
double c = acos(a + b);
vs.
code:
double a = cos(PI/2-lat1) * cos(PI/2- lat2);
double b = sin(PI/2-lat1) * sin(PI/2- lat2) * cos(lon2 - lon1);
double c = acos(a + b);
With the former, the problem occurs when calculating b. With the latter, it occurs calculating a.

I freely admit to being clueless about assembler (hell, I'm not even a programmer/CS guy) but if I step through the disassembly, one line seems to be doing it
code:
00436DB4  fld         qword ptr [PI (48E970h)]
When it loads PI (const double, still a valid value) onto the FPU stack, it usually works. The NaN appears because sometimes ST(0) gets set to 1#IND instead!

POKEMAN SAM
Jul 8, 2004

Grazing Occultation posted:

It's all using doubles and the default calling convention. The functions in question aren't even DLL-exported. I could handle getting this sort of weird voodoo poo poo if I had the balls to do things like that! :v:

I've noticed two really strange things:

If I put only one trig call on a line then the problem does not occur. For example:
code:
double a1 = cos(PI/2-lat1);
double a2 = cos(PI/2- lat2);
double a = a1 * a2;
double b = sin(PI/2-lat1) * sin(PI/2- lat2) * cos(lon2 - lon1);
double c = acos(a + b);
vs.
code:
double a = cos(PI/2-lat1) * cos(PI/2- lat2);
double b = sin(PI/2-lat1) * sin(PI/2- lat2) * cos(lon2 - lon1);
double c = acos(a + b);
With the former, the problem occurs when calculating b. With the latter, it occurs calculating a.

I freely admit to being clueless about assembler (hell, I'm not even a programmer/CS guy) but if I step through the disassembly, one line seems to be doing it
code:
00436DB4  fld         qword ptr [PI (48E970h)]
When it loads PI (const double, still a valid value) onto the FPU stack, it usually works. The NaN appears because sometimes ST(0) gets set to 1#IND instead!

What are the assembly outputs for the one-line vs multi-line snippets?

Jose Cuervo
Aug 25, 2004
This is a simple question but one I did not find the answer to from quick googling. Is there a difference between declaring a variable as long int myVariable and int long myVariable? And if there is, what is the difference?

That Turkey Story
Mar 30, 2003

Jose Cuervo posted:

This is a simple question but one I did not find the answer to from quick googling. Is there a difference between declaring a variable as long int myVariable and int long myVariable? And if there is, what is the difference?

No. It's also the same as simply "long".

Ciaphas
Nov 20, 2005

> BEWARE, COWARD :ovr:


I know glibc has backtrace() and backtrace_symbols, but does anyone know of a way to get the current backtrace (even if the symbols are mangled, I don't much care) under Sun CC (Sun Studio's compiler)? Google is proving useless, which doesn't bode well for this question. :smith:

Grazing Occultation
Aug 18, 2009

by angerbutt

Ugg boots posted:

What are the assembly outputs for the one-line vs multi-line snippets?

Two line:
code:
double a1 = cos(PI/2-lat1);
00436D4E  fld         qword ptr [PI (48E970h)] 
00436D54  fdiv        qword ptr [__real@4000000000000000 (48E988h)] 
00436D5A  fsub        qword ptr [lat1] 
00436D5D  sub         esp,8 
00436D60  fstp        qword ptr [esp] 
00436D63  call        @ILT+4645(_cos) (42F22Ah) 
00436D68  add         esp,8 
00436D6B  fstp        qword ptr [a1] 
double a2 = cos(PI/2- lat2);
00436D6E  fld         qword ptr [PI (48E970h)] 
00436D74  fdiv        qword ptr [__real@4000000000000000 (48E988h)] 
00436D7A  fsub        qword ptr [lat2] 
00436D7D  sub         esp,8 
00436D80  fstp        qword ptr [esp] 
00436D83  call        @ILT+4645(_cos) (42F22Ah) 
00436D88  add         esp,8 
00436D8B  fstp        qword ptr [a2] 
double a = a1 * a2;
00436D8E  fld         qword ptr [a1] 
00436D91  fmul        qword ptr [a2] 
00436D94  fstp        qword ptr [a]
One line:
code:
double a = cos(PI/2-lat1) * cos(PI/2- lat2);
00436D4E  fld         qword ptr [PI (48E970h)] 
00436D54  fdiv        qword ptr [__real@4000000000000000 (48E988h)] 
00436D5A  fsub        qword ptr [lat1] 
00436D5D  sub         esp,8 
00436D60  fstp        qword ptr [esp] 
00436D63  call        @ILT+4645(_cos) (42F22Ah) 
00436D68  add         esp,8 
00436D6B  fld         qword ptr [PI (48E970h)] 
00436D71  fdiv        qword ptr [__real@4000000000000000 (48E988h)] 
00436D77  fsub        qword ptr [lat2] 
00436D7A  sub         esp,8 
00436D7D  fstp        qword ptr [esp] 
00436D80  fstp        qword ptr [ebp-108h] 
00436D86  call        @ILT+4645(_cos) (42F22Ah) 
00436D8B  add         esp,8 
00436D8E  fmul        qword ptr [ebp-108h] 
00436D94  fstp        qword ptr [a]
Calculating b, for completeness:
code:
double b = sin(PI/2-lat1) * sin(PI/2- lat2) * cos(lon2 - lon1);
00436D97  fld         qword ptr [PI (48E970h)] 
00436D9D  fdiv        qword ptr [__real@4000000000000000 (48E988h)] 
00436DA3  fsub        qword ptr [lat1] 
00436DA6  sub         esp,8 
00436DA9  fstp        qword ptr [esp] 
00436DAC  call        @ILT+3925(_sin) (42EF5Ah) 
00436DB1  add         esp,8 
00436DB4  fld         qword ptr [PI (48E970h)] 
00436DBA  fdiv        qword ptr [__real@4000000000000000 (48E988h)] 
00436DC0  fsub        qword ptr [lat2] 
00436DC3  sub         esp,8 
00436DC6  fstp        qword ptr [esp] 
00436DC9  fstp        qword ptr [ebp-108h] 
00436DCF  call        @ILT+3925(_sin) (42EF5Ah) 
00436DD4  add         esp,8 
00436DD7  fmul        qword ptr [ebp-108h] 
00436DDD  fld         qword ptr [lon2] 
00436DE0  fsub        qword ptr [lon1] 
00436DE3  sub         esp,8 
00436DE6  fstp        qword ptr [esp] 
00436DE9  fstp        qword ptr [ebp-110h] 
00436DEF  call        @ILT+4645(_cos) (42F22Ah) 
00436DF4  add         esp,8 
00436DF7  fmul        qword ptr [ebp-110h] 
00436DFD  fstp        qword ptr [b] 

Bob Morales
Aug 18, 2006


Just wear the fucking mask, Bob

I don't care how many people I probably infected with COVID-19 while refusing to wear a mask, my comfort is far more important than the health and safety of everyone around me!

:psyduck:

Objective-C: Are they serious?

Mustach
Mar 2, 2003

In this long line, there's been some real strange genes. You've got 'em all, with some extras thrown in.
Serious about what?

Mr.Radar
Nov 5, 2005

You guys aren't going to believe this, but that guy is our games teacher.

Grazing Occultation posted:

It's all using doubles and the default calling convention. The functions in question aren't even DLL-exported. I could handle getting this sort of weird voodoo poo poo if I had the balls to do things like that! :v:

I've noticed two really strange things:

If I put only one trig call on a line then the problem does not occur. For example:
code:
double a1 = cos(PI/2-lat1);
double a2 = cos(PI/2- lat2);
double a = a1 * a2;
double b = sin(PI/2-lat1) * sin(PI/2- lat2) * cos(lon2 - lon1);
double c = acos(a + b);
vs.
code:
double a = cos(PI/2-lat1) * cos(PI/2- lat2);
double b = sin(PI/2-lat1) * sin(PI/2- lat2) * cos(lon2 - lon1);
double c = acos(a + b);
With the former, the problem occurs when calculating b. With the latter, it occurs calculating a.

I freely admit to being clueless about assembler (hell, I'm not even a programmer/CS guy) but if I step through the disassembly, one line seems to be doing it
code:
00436DB4  fld         qword ptr [PI (48E970h)]
When it loads PI (const double, still a valid value) onto the FPU stack, it usually works. The NaN appears because sometimes ST(0) gets set to 1#IND instead!

I don't know what's going wrong with your code, but have you tried setting your compiler to use SSE for floating-point math? (Most x86 compilers default to the old x87 instructions for compatibility, but if you don't need to run your code on anything older than an Athlon64 or a Pentium 4 there should be no problem and it might even increase performance). If the problem stops, then hey you've papered over it (but please leave a comment in the code explaining what's up for the next guy who has to try to fix it). If it doesn't, it might give you some more clues as to what's going on.

Mr.Radar fucked around with this message at 20:43 on Sep 28, 2010

MutantBlue
Jun 8, 2001

Grazing Occultation posted:

code:
double a = cos(PI/2-lat1) * cos(PI/2- lat2);
double b = sin(PI/2-lat1) * sin(PI/2- lat2) * cos(lon2 - lon1);
double c = acos(a + b);

This has nothing to do with the problem you're having but why do you keep recomputing PI/2? You could save a lot of divisions by defining a PI_2 constant or something.

That Turkey Story
Mar 30, 2003

MutantBlue posted:

This has nothing to do with the problem you're having but why do you keep recomputing PI/2? You could save a lot of divisions by defining a PI_2 constant or something.

PI/2 should be calculated at compile-time anyway.

Bob Morales
Aug 18, 2006


Just wear the fucking mask, Bob

I don't care how many people I probably infected with COVID-19 while refusing to wear a mask, my comfort is far more important than the health and safety of everyone around me!

MutantBlue posted:

This has nothing to do with the problem you're having but why do you keep recomputing PI/2? You could save a lot of divisions by defining a PI_2 constant or something.

Already done for you in math.h

MutantBlue
Jun 8, 2001

^^^I'm slow today.

That Turkey Story posted:

PI/2 should be calculated at compile-time anyway.

Or use M_PI_2 from math.h

It would avoid those fdiv ops in Grazing Occultation's assembly listing.

MutantBlue fucked around with this message at 21:08 on Sep 28, 2010

roomforthetuna
Mar 22, 2005

I don't need to know anything about virii! My CUSTOM PROGRAM keeps me protected! It's not like they'll try to come in through the Internet or something!
Isn't a compiler supposed to deal with dividing a constant by another constant such that it doesn't end up in the assembler code? (Like the turkey said but the assembly listings don't reflect.)

I would have thought that would be the case even in non-optimised builds.

MutantBlue
Jun 8, 2001

roomforthetuna posted:

Isn't a compiler supposed to deal with dividing a constant by another constant such that it doesn't end up in the assembler code?

Maybe he compiled in debug mode or with optimizations off.

ehnus
Apr 16, 2003

Now you're thinking with portals!

Grazing Occultation posted:

Two line:
code:
assembly

1#IND is generally a division by zero. By any chance do the values at the memory location of the constant 2.0 (0x48E988 in this case) ever get trampled on?

Pooball
Sep 21, 2005
Warm and squishy.
Also cos(PI/2-x) = sin(x)

(in fact you can get away with just using acos on some polynomial in cos(lat1+lat2) and cos(lat1-lat2))

edit: I know this doesn't shed light on this particular problem, but you should be careful about what you pass to acos, even when the argument is correct in exact arithmetic. For example on my system sin(0.08)^2+cos(0.08)^2 > 1.

Pooball fucked around with this message at 12:52 on Sep 29, 2010

covener
Jan 10, 2004

You know, for kids!

Ledneh posted:

I know glibc has backtrace() and backtrace_symbols, but does anyone know of a way to get the current backtrace (even if the symbols are mangled, I don't much care) under Sun CC (Sun Studio's compiler)? Google is proving useless, which doesn't bode well for this question. :smith:

comments in a diagnostic module I own says solaris 9 and higher has printstack(fd);

Grazing Occultation
Aug 18, 2009

by angerbutt
Thanks for the help guys. You're keeping me from giving up and heading for the pub!

MutantBlue posted:

Maybe he compiled in debug mode or with optimizations off.

Yep. I'm not gifted enough to navigate this poo poo without a fancy shmancy IDE!

ehnus posted:

1#IND is generally a division by zero. By any chance do the values at the memory location of the constant 2.0 (0x48E988 in this case) ever get trampled on?

That memory location is still valid. The memory storing PI is also valid; it's when it does the fld that it fails. I can see PI in the debugger and I can see it in the memory viewer, but as soon as it tries to push that value onto the FPU stack the top of the stack becomes 1#IND.

This happens in preparing for the second trig call, never the first. It loads PI correctly just a few assembly lines before. Is it possible that the first call has corrupted something somehow?

Pooball posted:

Also cos(PI/2-x) = sin(x)

(in fact you can get away with just using acos on some polynomial in cos(lat1+lat2) and cos(lat1-lat2))

edit: I know this doesn't shed light on this particular problem, but you should be careful about what you pass to acos, even when the argument is correct in exact arithmetic. For example on my system sin(0.08)^2+cos(0.08)^2 > 1.

I don't think this function's ever been optimised except to minimise the number of trig calls. I'm trying to avoid changing this function for the moment: it's been hard just finding a place where I can get the bug to happen repeatably.

ehnus
Apr 16, 2003

Now you're thinking with portals!

Grazing Occultation posted:

That memory location is still valid. The memory storing PI is also valid; it's when it does the fld that it fails. I can see PI in the debugger and I can see it in the memory viewer, but as soon as it tries to push that value onto the FPU stack the top of the stack becomes 1#IND.

This happens in preparing for the second trig call, never the first. It loads PI correctly just a few assembly lines before. Is it possible that the first call has corrupted something somehow?

What's the 64-bit hex value of the value that ends up on the fp-stack?

Pooball
Sep 21, 2005
Warm and squishy.
If the floating-point stack is full, then fld will store nan. So this program (using gcc asm):

code:
double pi = 3.142;

int main() {
  int n;
  for (n=0; n<9; n++)
    __asm__("fld %0" : : "m"(pi));
  return 0;
}
results in these registers:

code:
st0            -nan(0xc000000000000000)	(raw 0xffffc000000000000000)
st1            -66188687651123427803136	(raw 0xc04ae041890000000000)
st2            -66188687651123427803136	(raw 0xc04ae041890000000000)
st3            -66188687651123427803136	(raw 0xc04ae041890000000000)
st4            -66188687651123427803136	(raw 0xc04ae041890000000000)
st5            -66188687651123427803136	(raw 0xc04ae041890000000000)
st6            -66188687651123427803136	(raw 0xc04ae041890000000000)
st7            -66188687651123427803136	(raw 0xc04ae041890000000000)

Ciaphas
Nov 20, 2005

> BEWARE, COWARD :ovr:


covener posted:

comments in a diagnostic module I own says solaris 9 and higher has printstack(fd);

I tried that, yeah, but it was meant for C, and doesn't give me C++ names in a way that I can easily demangle. I ended up rolling my own stacktrace thingy using something like the following (not showing #includes because I don't remember what they were) [this code is also sort of incorrect, lots of casts and poo poo I'm not retyping from memory]
code:
   ...
   ucontext_t context;
   getcontext(&context);

   std::string stackTrace = "";
   walkcontext(&context, helper_func, &stackTrace); // calls helper_func once per item in callstack
   ...
extern "C" {
int helper_func(unsigned pc, int signal, void** extra)
{
   std::string& s = *((std::string*)extra);
   Dl_info stackinfo;
   int ret = dladdr(pc, &stackinfo);
   if (ret == 0)
   {
      s = "stack err: ";
      s += dlerror();
      s += '\n';
      return 1; // stop walking context
   }
   else
   {
      char buf[1024];
      cplus_demangle(stackinfo.dl_sname, buf, 1024);
      s += buf;
      s += '\n';
      return 0; // continue walking
   }
}
} // extern "C"
I wonder if it's possible to improve this trace to get the caller's line number (if available based on compile options), too. Any ideas, or is it completely impossible even with debug compiler flags?

Ciaphas fucked around with this message at 19:10 on Sep 29, 2010

OddObserver
Apr 3, 2009
Hmm. well, there is whatever addr2line does, too, if you don't mind linking to libbfd (which may be acceptable for purely debug stuff..)

Does the compiler in question use the same C++ ABI as gcc, BTW? If it does,
abi::__cxa_demangle might be of some use.

Ciaphas
Nov 20, 2005

> BEWARE, COWARD :ovr:


The compiler in question is Sun CC provided with Sun Studio 12.1. I don't really want to link anything that's going to murder production performance, either, though I want to be able to get the stack trace in production for the sake of more useful exception info.

pseudorandom name
May 6, 2007

Getting the source file and line number information would require interpreting the DWARF information, which is much more involved than just figuring out which dynamic symbol (probably) contains the address.

OddObserver
Apr 3, 2009

Ledneh posted:

The compiler in question is Sun CC provided with Sun Studio 12.1. I don't really want to link anything that's going to murder production performance, either, though I want to be able to get the stack trace in production for the sake of more useful exception info.

Well, the issue is more that libbfd is GPL'd, which is quite likely to be an issue for production stuff.

Hmm. An another option: if you have good control over production binaries, it may be possible to do the line number processing off-line, though you'd need to extract the memory map to know which libraries are located where. Sounds like one of those sorta-simple but pretty fiddly kind of tasks (again, addr2line code may be a good starting point for the post-processing tool).

pseudorandom name
May 6, 2007

libbfd doesn't have an ABI, either, which will make your life difficult when you upgrade. (It exists purely to share code between the various components of binutils, not to provide a public library. Not that that stops people from using it anyway, and causing much suffering.)

Optimus Prime Ribs
Jul 25, 2007

If I were to have a for loop, and for example have the test expression i < num(), will that function be called every time, or will a new const value be created to substitute it?

that awful man
Feb 18, 2007

YOSPOS, bitch

Optimus Prime Ribs posted:

If I were to have a for loop, and for example have the test expression i < num(), will that function be called every time, or will a new const value be created to substitute it?

It'll be called every time.

Blotto Skorzany
Nov 7, 2008

He's a PSoC, loose and runnin'
came the whisper from each lip
And he's here to do some business with
the bad ADC on his chip
bad ADC on his chiiiiip

Optimus Prime Ribs posted:

If I were to have a for loop, and for example have the test expression i < num(), will that function be called every time, or will a new const value be created to substitute it?

http://codepad.org/kc4dtWTZ

Optimus Prime Ribs
Jul 25, 2007

Well that makes sense.
Thanks guys.

Adbot
ADBOT LOVES YOU

pseudorandom name
May 6, 2007

http://codepad.org/TuWkRENh

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply