Update: the previous code does not accurately describe my problem. Now changed:
Below is a few lines of code of my program (C++ on Linux). My program often (but not always) crashes at the lines LINE_A and LINE_B
class A{
//...
map<int, const string> m_int2StrMap;
//...
void problematic(){
//...insert and erase of m_int2StrMap are involved
char temp[6] = {0};
int key = 12345;
//...temp's elements and key might be changed
map<int, const string>::iterator iter = this->m_int2StrMap.find(key);
if(iter != this->m_int2StrMap.end())
{
/*LINE_A*/ this->m_int2StrMap.erase(iter);
}
/*LINE_B*/ this->m_int2StrMap.insert(pair<int, const string>(key, string ((const char*)temp, 6)));
//...
}
//...
}
I used gdb to backtrack the core dumps generated, some gave me this info:
(gdb) bt
#0 0x00ae0410 in __kernel_vsyscall ()
#1 0x00138df0 in vfprintf () from /lib/libc.so.6
#2 0x0013a701 in vfprintf () from /lib/libc.so.6
#3 0x0017128b in __wcstod_internal () from /lib/libc.so.6
#4 0x00179595 in ____wcstof_l_internal () from /lib/libc.so.6
#5 0x001799d9 in ____wcstof_l_internal () from /lib/libc.so.6
#6 0x05bfd581 in operator delete () from /usr/lib/libstdc++.so.6
#7 0x05bda14d in std::string::_Rep::_M_destroy () from /usr/lib/libstdc++.so.6
#8 0x08084bf7 in problematic() at /usr/lib/gcc/i386-redhat-linux/4.1.2/../../../../include/c++/4.1.2/bits/basic_string.h:233
and some showed:
(gdb) bt
#0 0x00b81410 in __kernel_vsyscall ()
#1 0x009ccdf0 in vfprintf () from /lib/libc.so.6
#2 0x009ce701 in vfprintf () from /lib/libc.so.6
#3 0x00a0528b in __wcstod_internal () from /lib/libc.so.6
#4 0x00a0e250 in ____wcstof_l_internal () from /lib/libc.so.6
#5 0x00a0fd87 in wcsxfrm_l () from /lib/libc.so.6
#6 0x05bfeab7 in operator new () from /usr/lib/libstdc++.so.6
#7 0x05bda0fb in std::string::_Rep::_S_create () from /usr/lib/libstdc++.so.6
#8 0x05bdaef5 in ?? () from /usr/lib/libstdc++.so.6
#9 0x05bdaff1 in std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string () from /usr/lib/libstdc++.so.6
#10 0x080843e4 in probblematic() at bad.cpp:LINE_B
Is there anything unsafe in my code shown above? Thanks
Related
I understand the "big idea" behind stack traces, but how would one actually go about using a stack trace to determine the cause of a crash? For example, I'm coding a c++ macro, and my interpreter spits out the following stack trace:
> *** Break *** illegal instruction
>
>
>
> =========================================================== There was a crash. This is the entire stack trace of all threads:
> ===========================================================
> #0 0x00007fb64ef29bbc in waitpid () from /lib64/libc.so.6
> #1 0x00007fb64eea7ea2 in do_system () from /lib64/libc.so.6
> #2 0x00007fb64ffb3d84 in TUnixSystem::StackTrace (this=0x98e960) at /cvmfs/myusername/trunk/centos7/source/root-6.16.00/core/unix/src/TUnixSystem.cxx:2413
> #3 0x00007fb64ffb64bc in TUnixSystem::DispatchSignals (this=0x98e960, sig=kSigIllegalInstruction) at
> /cvmfs/myusername/trunk/centos7/source/root-6.16.00/core/unix/src/TUnixSystem.cxx:3644
> #4 <signal handler called>
> #5 0x00007fb65057f54a in ?? ()
> #6 0x000000000146e4ec in ?? ()
> #7 0x000000000028dc9d in ?? ()
> #8 0x0028dcf80146e51c in ?? ()
> #9 0x00007ffd08a37890 in ?? ()
> #10 0x0000000000051b93 in ?? ()
> #11 0x000000000000a372 in ?? ()
> #12 0x0000a3e900051c01 in ?? ()
> #13 0x000000000000146e in ?? ()
> #14 0x00007ffd08a3788f in ?? ()
> #15 0x0000000000000000 in ?? ()
> ===========================================================
>
>
> The lines below might hint at the cause of the crash. You may get help
> by asking at the ROOT forum http://root.cern.ch/forum Only if you are
> really convinced it is a bug in ROOT then please submit a report at
> http://root.cern.ch/bugs Please post the ENTIRE stack trace from above
> as an attachment in addition to anything else that might help us
> fixing this issue.
> ===========================================================
> #5 0x00007fb65057f54a in ?? ()
> #6 0x000000000146e4ec in ?? ()
> #7 0x000000000028dc9d in ?? ()
> #8 0x0028dcf80146e51c in ?? ()
> #9 0x00007ffd08a37890 in ?? ()
> #10 0x0000000000051b93 in ?? ()
> #11 0x000000000000a372 in ?? ()
> #12 0x0000a3e900051c01 in ?? ()
> #13 0x000000000000146e in ?? ()
> #14 0x00007ffd08a3788f in ?? ()
> #15 0x0000000000000000 in ?? ()
> ===========================================================
The idea behind the stack trace is to see who was calling who at the moment of the crash. So you can check the function producing the fault and see how it was called.
What you are seeing in your example are the address of your functions, of course if the stack is corrupted somehow you could find invalid addresses as well.
To see more useful information you should compile your code with debugging information, that way you will not only see the address but the name of the function too.
In gcc for example you enable debugging information with '-g', ie:
gcc -g test.c -o test
Here is an example of the 'test.c' faulty code:
void foo()
{
int a=1/0;
}
void foo1()
{
foo();
}
void foo2()
{
foo1();
}
void foo3()
{
foo2();
}
int main()
{
foo3();
};
this is the backtrace it produces (running in gdb, info is available because I used -g flag for the compilation)
#0 0x0000000000401101 in foo () at test.c:3
#1 0x0000000000401117 in foo1 () at test.c:7
#2 0x0000000000401128 in foo2 () at test.c:11
#3 0x0000000000401139 in foo3 () at test.c:15
#4 0x000000000040114a in main () at test.c:20
#0 0x0000003d7e432925 in raise () from /lib64/libc.so.6
#1 0x0000003d7e43408d in abort () from /lib64/libc.so.6
#2 0x00007ff601e3ba55 in os::abort(bool) () from /usr/java/jdk1.7.0_67-cloudera/jre/lib/amd64/server/libjvm.so
#3 0x00007ff601fbbf87 in VMError::report_and_die() () from /usr/java/jdk1.7.0_67-cloudera/jre/lib/amd64/server/libjvm.so
#4 0x00007ff601e4096f in JVM_handle_linux_signal () from /usr/java/jdk1.7.0_67-cloudera/jre/lib/amd64/server/libjvm.so
#5 <signal handler called>
#6 0x00007ff5fe8f218e in LdiInterFromArray () from /home/zhaojuan/project/DataType/thirdparty/occi-11.2/lib/libclntsh.so.11.1
#7 0x00007ff5ff85a1eb in kpcceiyd2iyd () from /home/zhaojuan/project/DataType/thirdparty/occi-11.2/lib/libclntsh.so.11.1
#8 0x00007ff600138c1d in ttccfpg () from /home/zhaojuan/project/DataType/thirdparty/occi-11.2/lib/libclntsh.so.11.1
#9 0x00007ff600136e90 in ttcfour () from /home/zhaojuan/project/DataType/thirdparty/occi-11.2/lib/libclntsh.so.11.1
#10 0x00007ff5fe5c45f3 in kpufcpf () from /home/zhaojuan/project/DataType/thirdparty/occi-11.2/lib/libclntsh.so.11.1
#11 0x00007ff5fe5c2872 in kpufch0 () from /home/zhaojuan/project/DataType/thirdparty/occi-11.2/lib/libclntsh.so.11.1
#12 0x00007ff5fe5c110f in kpufch () from /home/zhaojuan/project/DataType/thirdparty/occi-11.2/lib/libclntsh.so.11.1
#13 0x00007ff5fe556a03 in OCIStmtFetch () from /home/zhaojuan/project/DataType/thirdparty/occi-11.2/lib/libclntsh.so.11.1
#14 0x00007ff600a29b33 in oracle::occi::ResultSetImpl::next(unsigned int) () from /home/zhaojuan/project/DataType/thirdparty/occi-11.2/lib/libocci.so.11.1
#15 0x0000000000c6f481 in xcloud::xos::OracleLoader::RunLoadMain (this=0x78e6680) at /home/zhaojuan/project/DataType/be/src/exec_xos/OracleLoader.cpp:366
#16 0x0000000000c70f49 in xcloud::xos::OracleLoaderThread (This=<value optimized out>)
at /home/zhaojuan/project/DataType/be/src/exec_xos/OracleLoader.cpp:43
#17 0x0000003d7e8079d1 in start_thread () from /lib64/libpthread.so.0
#18 0x0000003d7e4e8b6d in clone () from /lib64/libc.so.6
hi , I met a occi problem, I want to get interval value from oracle, but it was core dump ,why?
the simplest code is
if (ttype == oracle::occi::OCCI_SQLT_INTERVAL_YM) {
LOG(INFO) << "set YM buffer";
m_res->setDataBuffer(i + 1, m_colBuf[i], ttype, 5, &(m_resultInfos[i].fieldLens[0]),
&(m_resultInfos[i].fieldFlags[0]), &(m_resultInfos[i].fieldRCs[0]));
} else if (ttype == oracle::occi::OCCI_SQLT_INTERVAL_DS) {
m_res->setDataBuffer(i + 1, m_colBuf[i], ttype, 11, &(m_resultInfos[i].fieldLens[0]),
&(m_resultInfos[i].fieldFlags[0]), &(m_resultInfos[i].fieldRCs[0]));
}
m_res is a type of oracle::occi::ResultSet*
m_colBuf[i] is alloc(colWidth[i] * fetchsize)
and it was crashed at:
if (0 == m_res->next(m_fetchSize)) {
LOG(INFO) << "Oracle Fetch finished!";
break;
}
I am trying to join the thread using boost and the program seems to wait indefinitely when I do that.
Thread->join();
On the console I see the following output when I break.
Program received signal SIGINT, Interrupt.
pthread_cond_wait##GLIBC_2.3.2 () at ../nptl/sysdeps/unix/sysv/linux/x86_64/pthread_cond_wait.S:185
185 ../nptl/sysdeps/unix/sysv/linux/x86_64/pthread_cond_wait.S: No such file or directory.
Here is the GDB trace.
#0 pthread_cond_wait##GLIBC_2.3.2 () at ../nptl/sysdeps/unix/sysv/linux/x86_64/pthread_cond_wait.S:185
#1 0x00000000004900a3 in boost::condition_variable::wait(boost::unique_lock<boost::mutex>&) () at /usr/local/boost/include/boost/thread/pthread/condition_variable.hpp:73
#2 0x00007ffff724e333 in boost::thread::join_noexcept() () at libs/thread/src/pthread/thread.cpp:316
#3 0x000000000048e135 in LIN::waitForThreads() () at /usr/local/boost/include/boost/thread/detail/thread.hpp:767
#4 0x00000000004882fd in fa::testinit::test_method() () at /home/ubuntu/UnitTest01.cpp:205
#5 0x0000000000488ef1 in fa::testinit_invoker() () at /home/ubuntu/UnitTest01.cpp:173
#6 0x000000000047296b in boost::detail::function::function_obj_invoker0<boost::detail::forward, int>::invoke(boost::detail::function::function_buffer&) ()
at /usr/local/boost/include/boost/function/function_template.hpp:771
#7 0x000000000044aabd in boost::execution_monitor::catch_signals(boost::function<int ()> const&) () at /usr/local/boost/include/boost/function/function_template.hpp:771
#8 0x000000000044ab91 in boost::execution_monitor::execute(boost::function<int ()> const&) () at /usr/local/boost/include/boost/test/impl/execution_monitor.ipp:1207
#9 0x000000000044b2d5 in boost::execution_monitor::vexecute(boost::function<void ()> const&) () at /usr/local/boost/include/boost/test/impl/execution_monitor.ipp:1313
#10 0x0000000000452bb2 in boost::unit_test::unit_test_monitor_t::execute_and_translate(boost::function<void ()> const&, unsigned int) () at /usr/local/boost/include/boost/test/impl/unit_test_monitor.ipp:46
#11 0x0000000000484760 in boost::unit_test::framework::state::execute_test_tree(unsigned long, unsigned int) () at /usr/local/boost/include/boost/test/impl/framework.ipp:685
#12 0x00000000004849ac in boost::unit_test::framework::state::execute_test_tree(unsigned long, unsigned int) () at /usr/local/boost/include/boost/test/impl/framework.ipp:636
#13 0x0000000000460329 in boost::unit_test::framework::run(unsigned long, bool) () at /usr/local/boost/include/boost/test/impl/framework.ipp:636
#14 0x0000000000460957 in boost::unit_test::unit_test_main(boost::unit_test::test_suite* (*)(int, char**), int, char**) () at usr/local/boost/include/boost/test/impl/unit_test_main.ipp:228
#15 0x00007ffff6673aed in __libc_start_main (main=0x4419c0 <main>, argc=1, argv=0x7fffffffe6c8, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffe6b8) at libc-start.c:269
#16 0x0000000000442ec9 in _start () at ../sysdeps/x86_64/start.S:122
waitForThreads code :
{
stopthread = true;
//wait for Thread 2
boost::mutex::scoped_lock lock(thread2_lock);
lock.unlock();
m_ConditionVariable.notify_one();
m_Thread->join();
}
Thread 2 code:
while(!stopthread){
boost::mutex::scoped_lock lock(thread2_lock);
while (m_Queue.empty())
{
m_ConditionVariable.wait(lock);
}
if (!m_Queue.empty()){
struct queue_output out;
out = m_Queue.front();
m_Queue.pop_front();
boost::mutex::scoped_lock new_lock(another_lock);
if (write(fd, &out, sizeof(struct queue_output)) == -1)
close(out.fd);
}
}
code to update queue:
{
boost::mutex::scoped_lock lock(thread2_lock);
m_Queue.push_back(input);
lock.unlock();
m_ConditionVariable.notify_one();
}
In UnitTest01.cpp, testinit first I am creating the thread, then updating the queue, then calling join.
Can someone please help me understand why it is hanging while thread joining?
Thanks in advance.
I use Poco-libraries parallel socket acceptor in my application and it sometimes crashes. Here is the backtrace of my application:
Program terminated with signal SIGABRT, Aborted.
#0 0x00007f9ed30ee107 in __GI_raise (sig=sig#entry=6) at ../nptl/sysdeps/unix/sysv/linux/raise.c:56
56 ../nptl/sysdeps/unix/sysv/linux/raise.c: No such file or directory.
(gdb) bt
#0 0x00007f9ed30ee107 in __GI_raise (sig=sig#entry=6) at ../nptl/sysdeps/unix/sysv/linux/raise.c:56
#1 0x00007f9ed30ef4e8 in __GI_abort () at abort.c:89
#2 0x00007f9ed312c044 in __libc_message (do_abort=do_abort#entry=1,
fmt=fmt#entry=0x7f9ed321ec60 "*** Error in `%s': %s: 0x%s ***\n") at ../sysdeps/posix/libc_fatal.c:175
#3 0x00007f9ed313181e in malloc_printerr (action=1, str=0x7f9ed321f000 "malloc(): memory corruption (fast)",
ptr=<optimized out>) at malloc.c:4996
#4 0x00007f9ed3133bbb in _int_malloc (av=0x7f9ecc000020, bytes=32) at malloc.c:3359
#5 0x00007f9ed3134eb0 in __GI___libc_malloc (bytes=32) at malloc.c:2891
#6 0x00007f9ed39d82e8 in operator new(unsigned long) () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#7 0x0000000000471058 in Poco::Net::ParallelSocketAcceptor<BFSTcpServiceHandler, Poco::Net::SocketReactor>::createServiceHandler (this=0x7f9ed0e17d70, socket=...) at /usr/local/include/Poco/Net/ParallelSocketAcceptor.h:172
#8 0x00000000004709d2 in Poco::Net::ParallelSocketAcceptor<BFSTcpServiceHandler, Poco::Net::SocketReactor>::onAccept
(this=0x7f9ed0e17d70, pNotification=0x7f9ecc0009c0) at /usr/local/include/Poco/Net/ParallelSocketAcceptor.h:160
#9 0x0000000000472bfe in Poco::Observer<Poco::Net::ParallelSocketAcceptor<BFSTcpServiceHandler, Poco::Net::SocketReactor>, Poco::Net::ReadableNotification>::notify (this=0x7f9ecc001d20, pNf=0x7f9ecc0009c0)
at /usr/local/include/Poco/Observer.h:86
#10 0x00007f9ed4709c4b in Poco::NotificationCenter::postNotification(Poco::AutoPtr<Poco::Notification>) ()
from /usr/local/lib/libPocoFoundation.so.30
#11 0x00007f9ed43c6630 in Poco::Net::SocketNotifier::dispatch(Poco::Net::SocketNotification*) ()
from /usr/local/lib/libPocoNet.so.30
#12 0x00007f9ed43c38a4 in Poco::Net::SocketReactor::dispatch(Poco::AutoPtr<Poco::Net::SocketNotifier>&, Poco::Net::SocketNotification*) () from /usr/local/lib/libPocoNet.so.30
#13 0x00007f9ed43c3d1b in Poco::Net::SocketReactor::dispatch(Poco::Net::Socket const&, Poco::Net::SocketNotification*)
() from /usr/local/lib/libPocoNet.so.30
#14 0x00007f9ed43c4910 in Poco::Net::SocketReactor::run() () from /usr/local/lib/libPocoNet.so.30
#15 0x000000000046a8dc in BFSTcpServer::run () at src/BFSTcpServer.cpp:69
#16 0x0000000000459c1b in std::_Bind_simple<void (*())()>::_M_invoke<>(std::_Index_tuple<>) (this=0x1ee8d38)
at /usr/include/c++/4.9/functional:1700
---Type <return> to continue, or q <return> to quit---
#17 0x0000000000459b63 in std::_Bind_simple<void (*())()>::operator()() (this=0x1ee8d38)
at /usr/include/c++/4.9/functional:1688
#18 0x0000000000459ae0 in std::thread::_Impl<std::_Bind_simple<void (*())()> >::_M_run() (this=0x1ee8d20)
at /usr/include/c++/4.9/thread:115
#19 0x00007f9ed3a2f970 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#20 0x00007f9ed4eaa0a4 in start_thread (arg=0x7f9ed0e18700) at pthread_create.c:309
#21 0x00007f9ed319eccd in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:111
I use the socket acceptor like this:
...
ServerSocket serverSocket(port);
reactor = new SocketReactor();
ParallelSocketAcceptor<BFSTcpServiceHandler,SocketReactor> acceptor(serverSocket, *reactor);
reactor->run();
...
And my servicehandler class is as follows:
class BFSTcpServiceHandler {
Poco::Net::StreamSocket socket;
Poco::Net::SocketReactor& reactor;
...
void onReadable(const Poco::AutoPtr<Poco::Net::ReadableNotification>& pNf);
...
public:
BFSTcpServiceHandler(Poco::Net::StreamSocket& _socket,
Poco::Net::SocketReactor& _reactor);
virtual ~BFSTcpServiceHandler();
};
//And implementation:
BFSTcpServiceHandler::BFSTcpServiceHandler(StreamSocket& _socket,
SocketReactor& _reactor): socket(_socket),reactor(_reactor) {
//Register Callbacks
reactor.addEventHandler(socket, NObserver<BFSTcpServiceHandler,
ReadableNotification>(*this, &BFSTcpServiceHandler::onReadable));
}
BFSTcpServiceHandler::~BFSTcpServiceHandler() {
//Unregister Callbacks
reactor.removeEventHandler(socket, NObserver<BFSTcpServiceHandler,
ReadableNotification>(*this, &BFSTcpServiceHandler::onReadable));
//Close socket
try {
socket.shutdown();
socket.close();
}catch(Exception &e){
LOG(ERROR)<<"ERROR IN CLOSING CONNECTION";
}
}
void BFSTcpServiceHandler::onReadable(
const Poco::AutoPtr<Poco::Net::ReadableNotification>& pNf) {
...
int read = socket.receiveBytes(_packet,sizeof(reqPacket.opCode));
...
//connection is served just close it!
delete this;
}
I am not sure if this is a bug in poco or my program. Either way, I will highly appreciate any comment or help. You can take a look at the full source of mine here:
https://github.com/bshafiee/BFS/blob/master/src/BFSTcpServer.cpp#L57
https://github.com/bshafiee/BFS/blob/master/src/BFSTcpServiceHandler.cpp#L65
Compiler Info:
gcc (Debian 4.9.1-19) 4.9.1
Poco Verision:
1.6.0 (2014-12-22)
My linux(ubuntu 12.04) process crash when I use ACE_5.7.1. My code:
ACE_INET_Addr remote_addr(server_addr.c_str());
ACE_SOCK_Stream stream;
ACE_SOCK_Connector connector;
ACE_Time_Value to(1, 0), to2(2,0);
ret = connector.connect(stream, remote_addr, &to);
Stack info:
Program terminated with signal 6, Aborted
#0 0x00002b47daf8d425 in __GI_raise (sig=<optimized out>) at ../nptl/sysdeps/unix/sysv/linux/raise.c:64
64 ../nptl/sysdeps/unix/sysv/linux/raise.c: No such file or directory.
(gdb) bt
#0 0x00002b47daf8d425 in __GI_raise (sig=<optimized out>) at ../nptl/sysdeps/unix/sysv/linux/raise.c:64
#1 0x00002b47daf90b8b in __GI_abort () at abort.c:91
#2 0x00002b47dafcb39e in __libc_message (do_abort=2, fmt=0x2b47db0d2e3f "*** %s ***: %s terminated\n") at ../sysdeps/unix/sysv/linux/libc_fatal.c:201
#3 0x00002b47db061817 in __GI___fortify_fail (msg=0x2b47db0d2dd6 "buffer overflow detected") at fortify_fail.c:32
#4 0x00002b47db060710 in __GI___chk_fail () at chk_fail.c:29
#5 0x00002b47db0617ce in __fdelt_chk (d=<optimized out>) at fdelt_chk.c:26
#6 0x00002b47d9ee9c3b in is_set (handle=1537, this=0x2b48883e9d90) at /home/cfcheng/MSP4.0/source/source/engine/ivs/../../share/ACE-5.7.1/ace/Handle_Set.inl:84
#7 set_bit (handle=1537, this=0x2b48883e9d90) at /home/cfcheng/MSP4.0/source/source/engine/ivs/../../share/ACE-5.7.1/ace/Handle_Set.inl:103
#8 ACE::handle_timed_complete (h=1537, timeout=0x2b48883ea110, is_tli=0) at ACE.cpp:2547
#9 0x00002b47d9f4e5c7 in ACE_SOCK_Connector::complete (this=<optimized out>, new_stream=..., remote_sap=0x0, tv=<optimized out>) at SOCK_Connector.cpp:262
#10 0x00002b47d9f4e737 in ACE_SOCK_Connector::shared_connect_finish (this=0x2b48883ea17f, new_stream=..., timeout=0x2b48883ea110, result=-1) at SOCK_Connector.cpp:155
#11 0x000000000044f4a9 in res_update_work::send_notify(std::string const&, std::string const&) ()
#12 0x000000000044fe7e in res_update_work::batch_send(std::string const&, ACE_Time_Value const&, bool) ()
#13 0x00000000004506c5 in res_update_work::update_all_res() ()
#14 0x0000000000450ada in res_update_work::svc() ()
#15 0x00002b47d9f56427 in ACE_Task_Base::svc_run (args=0x3e5a490) at Task.cpp:275
#16 0x00002b47d9f578c4 in ACE_Thread_Adapter::invoke (this=0x3e5ae90) at Thread_Adapter.cpp:98
#17 0x00002b47dad41e9a in start_thread (arg=0x2b4888401700) at pthread_create.c:308
#18 0x00002b47db04accd in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:112
#19 0x0000000000000000 in ?? ()
Who know why these code cause exception.
Thanks very much.
You are passing automatic objects to connect , I suspect this is causing the problem. Try to allocate the objects passed to connect dynamically, or make them attributes of an object which is still alive after the function where you call connect terminates. The same with the connector object.