Esempio n. 1
0
bool CanTypeFitValueOrig(const U value) {
    const intmax_t bot_t = intmax_t(std::numeric_limits<T>::min() );
    const intmax_t bot_u = intmax_t(std::numeric_limits<U>::min() );
    const uintmax_t top_t = uintmax_t(std::numeric_limits<T>::max() );
    const uintmax_t top_u = uintmax_t(std::numeric_limits<U>::max() );
    return !( (bot_t > bot_u && value < static_cast<U> (bot_t)) || (top_t < top_u && value > static_cast<U> (top_t)) );        
}
Esempio n. 2
0
bool
Timer::add(Event* e, int id, int64_t cycle, void* param)
{
	const intptr_t key((intptr_t)e);
	auto ib = m_clients.find(key);

	if ( ib == m_clients.end() )
	{
		auto res = m_clients.insert(client_cont::value_type(key, event_cont()));
		if ( res.second == false )
		{
			PWLOGLIB("failed to insert timer event: e:%p id:%d cycle:%jd param:%p", e, id, intmax_t(cycle), param);
			return false;
		}

		event_cont& ec(res.first->second);

		// 이후 코드는 insert 성공 여부와 상관 없이 반복자가 무효하다.
		invalidateIterator();

		if ( false == ec.insert(event_cont::value_type(id, event_type(param, cycle, s_getNow()))).second )
		{
			PWLOGLIB("failed to insert timer event: e:%p id:%d cycle:%jd param:%p", e, id, intmax_t(cycle), param);
			m_clients.erase(ib);
			return false;
		}

		//PWTRACE("add new timer event: e:%p type:%s id:%d cycle:%jdms", e, typeid(*e).name(), id, cycle);
		return true;
	}

	auto& ec = ib->second;
	auto ib_event = ec.find(id);

	if ( ec.end() == ib_event )
	{
		if ( false == ec.insert(event_cont::value_type(id, event_type(param, cycle, s_getNow()))).second )
		{
			PWLOGLIB("failed to insert timer event: e:%p id:%d cycle:%jd param:%p", e, id, intmax_t(cycle), param);
			return false;
		}

		invalidateIterator();
		//PWTRACE("add new timer event: e:%p type:%s id:%d cycle:%jdms", e, typeid(*e).name(), id, cycle);
		return true;
	}

	// 반복자를 건들지 않았으므로 invalidateIterator를 호출하지 않는다.
	auto& et = ib_event->second;
	et.param = param;
	et.cycle = cycle;
	et.start = s_getNow();

	//PWTRACE("add new timer event: e:%p type:%s id:%d cycle:%jdms", e, typeid(*e).name(), id, cycle);
	return true;
}
static void
TestPrintSignedMax()
{
  PoisonOutput();
  sprintf(output, "%" PRIdMAX, intmax_t(-INTMAX_C(432157943248732)));
  MOZ_RELEASE_ASSERT(!strcmp(output, "-432157943248732"));

  PoisonOutput();
  sprintf(output, "%" PRIiMAX, intmax_t(INTMAX_C(325719232983)));
  MOZ_RELEASE_ASSERT(!strcmp(output, "325719232983"));
}
Esempio n. 4
0
/*
 * gjs_profiler_extract_maps:
 *
 * This function will write the mapped section information to the
 * capture file so that the callgraph builder can generate symbols
 * from the stack addresses provided.
 *
 * Returns: %TRUE if successful; otherwise %FALSE and the profile
 *   should abort.
 */
static bool
gjs_profiler_extract_maps(GjsProfiler *self)
{
    using AutoStrv = std::unique_ptr<char *, decltype(&g_strfreev)>;

    int64_t now = g_get_monotonic_time() * 1000L;

    g_assert(((void) "Profiler must be set up before extracting maps", self));

    GjsAutoChar path = g_strdup_printf("/proc/%jd/maps", intmax_t(self->pid));

    char *content_tmp;
    size_t len;
    if (!g_file_get_contents(path, &content_tmp, &len, nullptr))
      return false;
    GjsAutoChar content = content_tmp;

    AutoStrv lines(g_strsplit(content, "\n", 0), g_strfreev);

    for (size_t ix = 0; lines.get()[ix]; ix++) {
        char file[256];
        unsigned long start;
        unsigned long end;
        unsigned long offset;
        unsigned long inode;

        file[sizeof file - 1] = '\0';

        int r = sscanf(lines.get()[ix], "%lx-%lx %*15s %lx %*x:%*x %lu %255s",
                       &start, &end, &offset, &inode, file);
        if (r != 5)
            continue;

        if (strcmp("[vdso]", file) == 0) {
            offset = 0;
            inode = 0;
        }

        if (!sp_capture_writer_add_map(self->capture, now, -1, self->pid, start,
                                       end, offset, inode, file))
            return false;
    }

    return true;
}
Esempio n. 5
0
/**
 * gjs_profiler_start:
 * @self: A #GjsProfiler
 *
 * As expected, this starts the GjsProfiler.
 *
 * This will enable the underlying JS profiler and register a POSIX timer to
 * deliver SIGPROF on the configured sampling frequency.
 *
 * To reduce sampling overhead, #GjsProfiler stashes information about the
 * profile to be calculated once the profiler has been disabled. Calling
 * gjs_profiler_stop() will result in that delayed work to be completed.
 *
 * You should call gjs_profiler_stop() when the profiler is no longer needed.
 */
void
gjs_profiler_start(GjsProfiler *self)
{
    g_return_if_fail(self);
    if (self->running)
        return;

#ifdef ENABLE_PROFILER

    g_return_if_fail(!self->capture);

    struct sigaction sa = { 0 };
    struct sigevent sev = { 0 };
    struct itimerspec its = { 0 };
    struct itimerspec old_its;

    GjsAutoChar path = g_strdup(self->filename);
    if (!path)
        path = g_strdup_printf("gjs-%jd.syscap", intmax_t(self->pid));

    self->capture = sp_capture_writer_new(path, 0);

    if (!self->capture) {
        g_warning("Failed to open profile capture");
        return;
    }

    if (!gjs_profiler_extract_maps(self)) {
        g_warning("Failed to extract proc maps");
        g_clear_pointer(&self->capture, sp_capture_writer_unref);
        return;
    }

    self->stack_depth = 0;

    /* Setup our signal handler for SIGPROF delivery */
    sa.sa_flags = SA_RESTART | SA_SIGINFO;
    sa.sa_sigaction = gjs_profiler_sigprof;
    sigemptyset(&sa.sa_mask);

    if (sigaction(SIGPROF, &sa, nullptr) == -1) {
        g_warning("Failed to register sigaction handler: %s", g_strerror(errno));
        g_clear_pointer(&self->capture, sp_capture_writer_unref);
        return;
    }

    /*
     * Create our SIGPROF timer
     *
     * We want to receive a SIGPROF signal on the JS thread using our
     * configured sampling frequency. Instead of allowing any thread to be
     * notified, we set the _tid value to ensure that only our thread gets
     * delivery of the signal. This feature is generally just for
     * threading implementations, but it works for us as well and ensures
     * that the thread is blocked while we capture the stack.
     */
    sev.sigev_notify = SIGEV_THREAD_ID;
    sev.sigev_signo = SIGPROF;
    sev._sigev_un._tid = syscall(__NR_gettid);

    if (timer_create(CLOCK_MONOTONIC, &sev, &self->timer) == -1) {
        g_warning("Failed to create profiler timer: %s", g_strerror(errno));
        g_clear_pointer(&self->capture, sp_capture_writer_unref);
        return;
    }

    /* Calculate sampling interval */
    its.it_interval.tv_sec = 0;
    its.it_interval.tv_nsec = NSEC_PER_SEC / SAMPLES_PER_SEC;
    its.it_value.tv_sec = 0;
    its.it_value.tv_nsec = NSEC_PER_SEC / SAMPLES_PER_SEC;

    /* Now start this timer */
    if (timer_settime(self->timer, 0, &its, &old_its) != 0) {
        g_warning("Failed to enable profiler timer: %s", g_strerror(errno));
        timer_delete(self->timer);
        g_clear_pointer(&self->capture, sp_capture_writer_unref);
        return;
    }

    self->running = true;

    /* Notify the JS runtime of where to put stack info */
    js::SetContextProfilingStack(self->cx, self->stack, &self->stack_depth,
                                 G_N_ELEMENTS(self->stack));

    /* Start recording stack info */
    js::EnableContextProfilingStack(self->cx, true);

    g_message("Profiler started");

#else  /* !ENABLE_PROFILER */

    self->running = true;
    g_message("Profiler is disabled. Recompile with --enable-profiler to use.");

#endif  /* ENABLE_PROFILER */
}
Esempio n. 6
0
bool EmberRender(EmberOptions& opt)
{
#ifdef USECL	EmberCLns::OpenCLInfo& info(EmberCLns::OpenCLInfo::Instance());#endif
	std::cout.imbue(std::locale(""));

	if (opt.DumpArgs())
		cout << opt.GetValues(OPT_USE_RENDER) << endl;

	if (opt.OpenCLInfo())
	{
		cout << "\nOpenCL Info: " << endl;
		cout << info.DumpInfo();
		return true;
	}

	Timing t;
	bool writeSuccess = false;
	byte* finalImagep;
	uint padding;
	size_t i, channels;
	size_t strips;
	size_t iterCount;
	string filename;
	string inputPath = GetPath(opt.Input());
	ostringstream os;
	pair<size_t, size_t> p;
	vector<Ember<T>> embers;
	vector<byte> finalImage;
	EmberStats stats;
	EmberReport emberReport;
	EmberImageComments comments;
	XmlToEmber<T> parser;
	EmberToXml<T> emberToXml;
	vector<QTIsaac<ISAAC_SIZE, ISAAC_INT>> randVec;
	const vector<pair<size_t, size_t>> devices = Devices(opt.Devices());
	unique_ptr<RenderProgress<T>> progress(new RenderProgress<T>());
	unique_ptr<Renderer<T, float>> renderer(CreateRenderer<T>(opt.EmberCL() ? OPENCL_RENDERER : CPU_RENDERER, devices, false, 0, emberReport));
	vector<string> errorReport = emberReport.ErrorReport();

	if (!errorReport.empty())
		emberReport.DumpErrorReport();

	if (!renderer.get())
	{
		cout << "Renderer creation failed, exiting." << endl;
		return false;
	}

	if (opt.EmberCL() && renderer->RendererType() != OPENCL_RENDERER)//OpenCL init failed, so fall back to CPU.
		opt.EmberCL(false);

	if (!InitPaletteList<T>(opt.PalettePath()))
		return false;

	if (!ParseEmberFile(parser, opt.Input(), embers))
		return false;

	if (!opt.EmberCL())
	{
		if (opt.ThreadCount() == 0)
		{
			cout << "Using " << Timing::ProcessorCount() << " automatically detected threads." << endl;
			opt.ThreadCount(Timing::ProcessorCount());
		}
		else
		{
			cout << "Using " << opt.ThreadCount() << " manually specified threads." << endl;
		}

		renderer->ThreadCount(opt.ThreadCount(), opt.IsaacSeed() != "" ? opt.IsaacSeed().c_str() : nullptr);
	}
	else
	{
		cout << "Using OpenCL to render." << endl;

		if (opt.Verbose())
		{
			for (auto& device : devices)
			{
				cout << "Platform: " << info.PlatformName(device.first) << endl;
				cout << "Device: " << info.DeviceName(device.first, device.second) << endl;
			}
		}

		if (opt.ThreadCount() > 1)
			cout << "Cannot specify threads with OpenCL, using 1 thread." << endl;

		opt.ThreadCount(1);
		renderer->ThreadCount(opt.ThreadCount(), opt.IsaacSeed() != "" ? opt.IsaacSeed().c_str() : nullptr);

		if (opt.BitsPerChannel() != 8)
		{
			cout << "Bits per channel cannot be anything other than 8 with OpenCL, setting to 8." << endl;
			opt.BitsPerChannel(8);
		}
	}

	if (opt.Format() != "jpg" &&
		opt.Format() != "png" &&
		opt.Format() != "ppm" &&
		opt.Format() != "bmp")
	{
		cout << "Format must be jpg, png, ppm, or bmp not " << opt.Format() << ". Setting to jpg." << endl;
	}

	channels = opt.Format() == "png" ? 4 : 3;

	if (opt.BitsPerChannel() == 16 && opt.Format() != "png")
	{
		cout << "Support for 16 bits per channel images is only present for the png format. Setting to 8." << endl;
		opt.BitsPerChannel(8);
	}
	else if (opt.BitsPerChannel() != 8 && opt.BitsPerChannel() != 16)
	{
		cout << "Unexpected bits per channel specified " << opt.BitsPerChannel() << ". Setting to 8." << endl;
		opt.BitsPerChannel(8);
	}

	if (opt.InsertPalette() && opt.BitsPerChannel() != 8)
	{
		cout << "Inserting palette only supported with 8 bits per channel, insertion will not take place." << endl;
		opt.InsertPalette(false);
	}

	if (opt.AspectRatio() < 0)
	{
		cout << "Invalid pixel aspect ratio " << opt.AspectRatio() << endl << ". Must be positive, setting to 1." << endl;
		opt.AspectRatio(1);
	}

	if (!opt.Out().empty() && (embers.size() > 1))
	{
		cout << "Single output file " << opt.Out() << " specified for multiple images. Changing to use prefix of badname-changethis instead. Always specify prefixes when reading a file with multiple embers." << endl;
		opt.Out("");
		opt.Prefix("badname-changethis");
	}

	//Final setup steps before running.
	os.imbue(std::locale(""));
	padding = uint(std::log10(double(embers.size()))) + 1;
	renderer->EarlyClip(opt.EarlyClip());
	renderer->YAxisUp(opt.YAxisUp());
	renderer->LockAccum(opt.LockAccum());
	renderer->InsertPalette(opt.InsertPalette());
	renderer->PixelAspectRatio(T(opt.AspectRatio()));
	renderer->Transparency(opt.Transparency());
	renderer->NumChannels(channels);
	renderer->BytesPerChannel(opt.BitsPerChannel() / 8);
	renderer->Priority(eThreadPriority(Clamp<intmax_t>(intmax_t(opt.Priority()), intmax_t(eThreadPriority::LOWEST), intmax_t(eThreadPriority::HIGHEST))));
	renderer->Callback(opt.DoProgress() ? progress.get() : nullptr);

	for (i = 0; i < embers.size(); i++)
	{
		if (opt.Verbose() && embers.size() > 1)
			cout << "\nFlame = " << i + 1 << "/" << embers.size() << endl;
		else if (embers.size() > 1)
			VerbosePrint(endl);

		if (opt.Supersample() > 0)
			embers[i].m_Supersample = opt.Supersample();

		if (opt.SubBatchSize() != DEFAULT_SBS)
			embers[i].m_SubBatchSize = opt.SubBatchSize();

		embers[i].m_TemporalSamples = 1;//Force temporal samples to 1 for render.
		embers[i].m_Quality *= T(opt.QualityScale());
		embers[i].m_FinalRasW = size_t(T(embers[i].m_FinalRasW) * opt.SizeScale());
		embers[i].m_FinalRasH = size_t(T(embers[i].m_FinalRasH) * opt.SizeScale());
		embers[i].m_PixelsPerUnit *= T(opt.SizeScale());

		if (embers[i].m_FinalRasW == 0 || embers[i].m_FinalRasH == 0)
		{
			cout << "Output image " << i << " has dimension 0: " << embers[i].m_FinalRasW  << ", " << embers[i].m_FinalRasH << ". Setting to 1920 x 1080." << endl;
			embers[i].m_FinalRasW = 1920;
			embers[i].m_FinalRasH = 1080;
		}

		//Cast to double in case the value exceeds 2^32.
		double imageMem = double(renderer->NumChannels()) * double(embers[i].m_FinalRasW)
			   * double(embers[i].m_FinalRasH) * double(renderer->BytesPerChannel());
		double maxMem = pow(2.0, double((sizeof(void*) * 8) - 1));

		if (imageMem > maxMem)//Ensure the max amount of memory for a process is not exceeded.
		{
			cout << "Image " << i << " size > " << maxMem << ". Setting to 1920 x 1080." << endl;
			embers[i].m_FinalRasW = 1920;
			embers[i].m_FinalRasH = 1080;
		}

		stats.Clear();
		renderer->SetEmber(embers[i]);
		renderer->PrepFinalAccumVector(finalImage);//Must manually call this first because it could be erroneously made smaller due to strips if called inside Renderer::Run().

		if (opt.Strips() > 1)
		{
			strips = opt.Strips();
		}
		else
		{
			p = renderer->MemoryRequired(1, true, false);//No threaded write for render, only for animate.
			strips = CalcStrips(double(p.second), double(renderer->MemoryAvailable()), opt.UseMem());

			if (strips > 1)
				VerbosePrint("Setting strips to " << strips << " with specified memory usage of " << opt.UseMem());
		}

		strips = VerifyStrips(embers[i].m_FinalRasH, strips,
			[&](const string& s) { cout << s << endl; },//Greater than height.
			[&](const string& s) { cout << s << endl; },//Mod height != 0.
			[&](const string& s) { cout << s << endl; });//Final strips value to be set.

		//For testing incremental renderer.
		//int sb = 1;
		//bool resume = false, success = false;
		//do
		//{
		//	success = renderer->Run(finalImage, 0, sb, false/*resume == false*/) == RENDER_OK;
		//	sb++;
		//	resume = true;
		//}
		//while (success && renderer->ProcessState() != ACCUM_DONE);

		StripsRender<T>(renderer.get(), embers[i], finalImage, 0, strips, opt.YAxisUp(),
		[&](size_t strip)//Pre strip.
		{
			if (opt.Verbose() && (strips > 1) && strip > 0)
				cout << endl;

			if (strips > 1)
				VerbosePrint("Strip = " << (strip + 1) << "/" << strips);
		},
		[&](size_t strip)//Post strip.
		{
			progress->Clear();
			stats += renderer->Stats();
		},
		[&](size_t strip)//Error.
		{
			cout << "Error: image rendering failed, skipping to next image." << endl;
			renderer->DumpErrorReport();//Something went wrong, print errors.
		},
		//Final strip.
		//Original wrote every strip as a full image which could be very slow with many large images.
		//Only write once all strips for this image are finished.
		[&](Ember<T>& finalEmber)
		{
			if (!opt.Out().empty())
			{
				filename = opt.Out();
			}
			else if (opt.NameEnable() && !finalEmber.m_Name.empty())
			{
				filename = inputPath + opt.Prefix() + finalEmber.m_Name + opt.Suffix() + "." + opt.Format();
			}
			else
			{
				ostringstream fnstream;

				fnstream << inputPath << opt.Prefix() << setfill('0') << setw(padding) << i << opt.Suffix() << "." << opt.Format();
				filename = fnstream.str();
			}

			//TotalIterCount() is actually using ScaledQuality() which does not get reset upon ember assignment,
			//so it ends up using the correct value for quality * strips.
			iterCount = renderer->TotalIterCount(1);
			comments = renderer->ImageComments(stats, opt.PrintEditDepth(), opt.IntPalette(), opt.HexPalette());
			os.str("");
			os << comments.m_NumIters << " / " << iterCount << " (" << std::fixed << std::setprecision(2) << ((double(stats.m_Iters) / double(iterCount)) * 100) << "%)";

			VerbosePrint("\nIters ran/requested: " + os.str());
			if (!opt.EmberCL()) VerbosePrint("Bad values: " << stats.m_Badvals);
			VerbosePrint("Render time: " + t.Format(stats.m_RenderMs));
			VerbosePrint("Pure iter time: " + t.Format(stats.m_IterMs));
			VerbosePrint("Iters/sec: " << size_t(stats.m_Iters / (stats.m_IterMs / 1000.0)) << endl);
			VerbosePrint("Writing " + filename);

			if ((opt.Format() == "jpg" || opt.Format() == "bmp") && renderer->NumChannels() == 4)
				RgbaToRgb(finalImage, finalImage, renderer->FinalRasW(), renderer->FinalRasH());

			finalImagep = finalImage.data();
			writeSuccess = false;

			if (opt.Format() == "png")
				writeSuccess = WritePng(filename.c_str(), finalImagep, finalEmber.m_FinalRasW, finalEmber.m_FinalRasH, opt.BitsPerChannel() / 8, opt.PngComments(), comments, opt.Id(), opt.Url(), opt.Nick());
			else if (opt.Format() == "jpg")
				writeSuccess = WriteJpeg(filename.c_str(), finalImagep, finalEmber.m_FinalRasW, finalEmber.m_FinalRasH, int(opt.JpegQuality()), opt.JpegComments(), comments, opt.Id(), opt.Url(), opt.Nick());
			else if (opt.Format() == "ppm")
				writeSuccess = WritePpm(filename.c_str(), finalImagep, finalEmber.m_FinalRasW, finalEmber.m_FinalRasH);
			else if (opt.Format() == "bmp")
				writeSuccess = WriteBmp(filename.c_str(), finalImagep, finalEmber.m_FinalRasW, finalEmber.m_FinalRasH);

			if (!writeSuccess)
				cout << "Error writing " << filename << endl;
		});

		if (opt.EmberCL() && opt.DumpKernel())
		{
			if (auto rendererCL = dynamic_cast<RendererCL<T, float>*>(renderer.get()))
			{
				cout << "Iteration kernel:\n" <<
				rendererCL->IterKernel() << "\n\n" <<
				"Density filter kernel:\n" <<
				rendererCL->DEKernel() << "\n\n" <<
				"Final accumulation kernel:\n" <<
				rendererCL->FinalAccumKernel() << endl;
			}
		}

		VerbosePrint("Done.");
	}

	t.Toc("\nFinished in: ", true);

	return true;
}
Esempio n. 7
0
void
MsgChannel::eventReadData(size_t len)
{
	//PWSHOWMETHOD();
	do {
		switch(m_recv_state)
		{
		case RecvState::START:
		{
			//PWTRACE("RecvState::START: %p", this);
			m_recv.clear();
			m_recv_bodylen = 0;
			m_dest_bodylen = 0;
			m_recv_state = RecvState::HEADER;
		}// Fall to RecvState::HEADER
		/* no break */
		case RecvState::HEADER:
		{
			//PWTRACE("RecvState::HEADER: %p", this);
			if ( m_rbuf->getReadableSize() < size_t(MsgPacket::limit_type::MIN_HEADER_SIZE) )
			{
				//PWTRACE("not yet: min:%jd", intmax_t(MsgPacket::limit_type::MIN_HEADER_SIZE) );
				// 아직 헤더 크기를 받지 못함.
				return;
			}

			IoBuffer::blob_type b;
			m_rbuf->grabRead(b);
			const char* eol(PWStr::findLine(b.buf, b.size));
			if ( nullptr == eol )
			{
				if ( b.size > size_t(MsgPacket::limit_type::MAX_HEADER_SIZE) )
				{
					PWLOGLIB("too long header: input:%jd", intmax_t(b.size));
					m_recv_state = RecvState::ERROR;
					goto PROC_ERROR;
				}

				return;
			}

			const size_t cplen(eol - b.buf);
			if ( not m_recv.setHeader(b.buf, cplen) )
			{
				// invalid packet
				std::string out;
				PWEnc::encodeHex(out, b.buf, cplen);
				PWLOGLIB("invalid packet: ch:%p chtype:%s header:%s", this, typeid(*this).name(), out.c_str());

				m_rbuf->moveRead(cplen+2);

				m_recv_state = RecvState::ERROR;
				goto PROC_ERROR;
			}

			m_rbuf->moveRead(cplen+2);

			if ( (m_dest_bodylen = m_recv.getBodySize()) > 0 )
			{
				m_recv_state = RecvState::BODY;
			}
			else
			{
				//m_recv_state = RecvState::DONE;
				//PWTRACE("RecvState::DONE: %p", this);
				//eventReadPacket(m_recv, m_recv.m_body.buf, m_recv.m_body.size);
				hookReadPacket(m_recv, m_recv.m_body.buf, m_recv.m_body.size);
				m_recv_state = RecvState::START;
				break;
			}
		}// Fall to RecvState::BODY
		/* no break */
		case RecvState::BODY:
		{
			//PWTRACE("RecvState::BODY: %p", this);
			blob_type& body(m_recv.m_body);
			if ( body.buf == nullptr )
			{
				if ( not body.allocate(m_dest_bodylen) )
				{
					PWLOGLIB("not enough memory");
					m_recv_state = RecvState::ERROR;
					break;
				}
			}// if (body.buf)

			IoBuffer::blob_type b;
			m_rbuf->grabRead(b);
			const size_t cplen(std::min(b.size, (m_dest_bodylen - m_recv_bodylen)) );
			if ( cplen > 0 )
			{
				char* p(const_cast<char*>(body.buf) + m_recv_bodylen);
				::memcpy(p, b.buf, cplen);
				m_recv_bodylen += cplen;
				m_rbuf->moveRead(cplen);
			}

			// 아직 더 받아야할 패킷이 있으면 반환한다.
			if ( m_recv_bodylen not_eq m_dest_bodylen )
			{
				return;
			}

			m_recv_state = RecvState::DONE;
		}
		/* no break */
		case RecvState::DONE:
		{
			//PWTRACE("RecvState::DONE: %p", this);
			hookReadPacket(m_recv, m_recv.m_body.buf, m_recv.m_body.size);
			m_recv_state = RecvState::START;
			break;
		}// case RecvState::DONE
		case RecvState::ERROR:
		{
PROC_ERROR:
			//PWTRACE("RecvState::ERROR: %p", this);
			eventError(Error::INVALID_PACKET, 0);
			m_recv_state = RecvState::START;
			if ( isInstDeleteOrExpired() ) return;
		}
		/* no break */
		default:
		{
			PWLOGLIB("Invalid state: %d", static_cast<int>(m_recv_state));
			m_recv_state = RecvState::START;
			break;
		}
		}// switch(m_recv_state)
	} while ( true );
}
//------------------------------------------------------------------------------
GDChart & GDChart::createChart()
{
  create(width_,height_);

  bool isIntergerOnlyValues = true;
  intptr_t i, j, xCount = 0, x, y, x0 = 0, y0 = 0;
  // calc min max
  ldouble minValue = DBL_MAX, maxValue = -DBL_MAX;
  for( i = data_.count() - 1; i >= 0; i-- ){
    j = data_[i].count();
    xCount = tmax(xCount,j);
    const Array<ldouble> & data = data_[i];
    for( j = data.count() - 1; j >= 0; j-- ){
      volatile intmax_t v = intmax_t(data[j]);
      volatile ldouble lv = ldouble(v);
      if( lv != data[j] ) isIntergerOnlyValues = false;
      minValue = tmin(minValue,data[j]);
      maxValue = tmax(maxValue,data[j]);
    }
  }
  ldouble yAxis = (maxValue - minValue) / (height_ - topBorder_ - bottomBorder_);
  intptr_t leftBorderDelta = 0, rightBorderDelta = 0, topBorderDelta = 0, bottomBorderDelta = 0;
  // clear image
  fill(0,0,colorAllocate(255,255,255));
  // draw lines
  intptr_t lineColor = colorAllocate(230,230,230);
  // draw vert grid lines
  for( j = 0; j < xCount; j++ ){
    x = (width_ - leftBorder_ - rightBorder_) * j / (xCount - 1) + leftBorder_;
    line(x,topBorder_,x,height_ - bottomBorder_,lineColor);
  }
  intptr_t yLabelColor = makeColor(ldouble(j),ldouble(j),ldouble(j));
  for( y = topBorder_; uintptr_t(y) <= height_ - bottomBorder_; y += fontHeight(font_) * 2 ){
    ldouble v = maxValue - (y - topBorder_) * yAxis;
    // draw horiz grid line
    line(leftBorder_,y,width_ - rightBorder_,y,lineColor);
    // draw ylabel
    utf8::String label;
    if( isIntergerOnlyValues ){
      label = printTraffic(intmax_t(v),true);//utf8::String::print("%"PRIdPTR,intptr_t(v));
    }
    else {
      label = utf8::String::print("%.2"PRF_LDBL"f",v);
    }
    uintptr_t sz = label.size();
    x = leftBorder_ - sz * fontWidth(font_);
    string(GD::font(font_),x,y,label.c_str(),yLabelColor);
    if( x < 0 && -x > leftBorderDelta ) leftBorderDelta = -x;
  }
  // draw data lines
  for( i = 0; uintptr_t(i) < data_.count(); i++ ){
    intptr_t color = makeColor(ldouble(i + 1),ldouble(i + 1),ldouble(i + 1));
    const Array<ldouble> & data = data_[i];
    for( j = 0; uintptr_t(j) < data.count(); j++ ){
      x = (width_ - leftBorder_ - rightBorder_) * j / (xCount - 1) + leftBorder_;
      y = intptr_t(height_ - topBorder_ - bottomBorder_ - (data[j] - minValue) / yAxis) + topBorder_;
      if( j > 0 ) line(x0,y0,x,y,color);
      x0 = x;
      y0 = y;
    }
  }
  intptr_t xBarSize = 2, yBarSize = 2;
  intptr_t barColor = colorAllocate(255,0,0);
  intptr_t xLabelColor = makeColor(ldouble(i + 1),ldouble(i + 1),ldouble(i + 1));
  for( i = 0; uintptr_t(i) < data_.count(); i++ ){
    const Array<ldouble> & data = data_[i];
    for( j = 0; uintptr_t(j) < data.count(); j++ ){
      x = (width_ - leftBorder_ - rightBorder_) * j / (xCount - 1) + leftBorder_;
      y = intptr_t(height_ - topBorder_ - bottomBorder_ - (data[j] - minValue) / yAxis) + topBorder_;
      // draw bar
      filledRectangle(
        tmax(leftBorder_,uintptr_t(x - xBarSize)),
        tmax(topBorder_,uintptr_t(y - yBarSize)),
        tmin(width_ - rightBorder_,uintptr_t(x + xBarSize)),
        tmin(height_ - bottomBorder_,uintptr_t(y + yBarSize)),
        barColor
      );
      x0 = x;
      y0 = y;
      // draw xlabel
      y = height_ - bottomBorder_ + xBarSize;
      utf8::String label(utf8::String::print("%"PRIdPTR,intptr_t(j + xlvs_)));
      string(GD::font(font_),x + xBarSize,y,label.c_str(),xLabelColor);
      if( y + fontHeight(font_) >= height_ )
        bottomBorderDelta = y + fontHeight(font_) - height_ + 1;
      x = x + xBarSize + fontWidth(font_) * label.size();
      if( uintptr_t(x) >= width_ ) rightBorderDelta = x - width_ + 1;
    }
  }
  if( leftBorderDelta != 0 || rightBorderDelta != 0 || topBorderDelta != 0 || bottomBorderDelta != 0 ){
    GDChart chart(*this);
    chart.leftBorder_ += leftBorderDelta;
    chart.rightBorder_ += rightBorderDelta;
    chart.topBorder_ += topBorderDelta;
    chart.bottomBorder_ += bottomBorderDelta;
    chart.createChart();
    xchg(image_,chart.image_);
    xchg(png_,chart.png_);
    xchg(pngSize_,chart.pngSize_);
  }
  else {
    gdFree(png_);
    png_ = pngPtrEx(&pngSize_,9);
  }
  return *this;
}